当前位置:首页 > 科技  > 软件

要不要升级?Java 21强大的新特性,代码量减半

来源: 责编: 时间:2024-05-16 09:09:13 75观看
导读1. record模式Record模式由 JEP 405 作为预览功能提出,并在 JDK 19 中发布,JEP 432 进行了第二次预览,并在 JDK 20 中发布。该功能与用于switch的模式匹配(JEP 441)共同发展,两者之间有相当多的交互1.1 instanceof类型模式O

1. record模式

Record模式由 JEP 405 作为预览功能提出,并在 JDK 19 中发布,JEP 432 进行了第二次预览,并在 JDK 20 中发布。该功能与用于switch的模式匹配(JEP 441)共同发展,两者之间有相当多的交互EkT28资讯网——每日最新资讯28at.com

1.1 instanceof类型模式

Object obj = "Pack" ;// Java 16之前if (obj instanceof String) {    String s = (String) obj ;    System.out.println("强转为String") ;}// 自Java 16起if (obj instanceof String s) {    System.out.println("简便多了") ;}

在上面的代码中从java16开始,运行时obj的值是String的实例,则obj与类型模式String s匹配。如果模式匹配,则表达式的实例为true,并且模式变量s初始化为obj转换为String的值,然后可以在包含的代码块中使用该值。EkT28资讯网——每日最新资讯28at.com

1.2 模式匹配与Records

Records (JEP 395)是数据的透明载体。接收record类实例的代码通常将使用内置的组件访问器方法提取数据,称为组件。例如,我们可以使用类型模式来测试值是否是record类Point的实例,如果是,则从值中提取x和y组件:EkT28资讯网——每日最新资讯28at.com

// 自Java 16起public record Point(int x, int y) {}public static void main(String[] args) {  Object obj = new Point(10, 20);  if (obj instanceof Point p) {    int x = p.x();    int y = p.y();    System.out.println(x + y);  }}

上面的代码看着与1.1中介绍的没撒区别就是类型模式,在上面的代码中我们仅仅是访问了record类x与y的方法,如果是这样我们还可以像下面这样操作:EkT28资讯网——每日最新资讯28at.com

Object obj = new Point(10, 20) ;// 自java 21起if (obj instanceof Point(int x, int y)) {  System.out.println(x + y) ;}

这里的Point(int x, int y) 是一个record模式。它将提取组件的局部变量声明移至模式本身,并在值与模式匹配时通过调用访问器方法初始化这些变量。EkT28资讯网——每日最新资讯28at.com

1.3 嵌套record模式

有如下定义EkT28资讯网——每日最新资讯28at.com

public record Point(int x, int y) {}enum Color { RED, GREEN, BLUE }record ColoredPoint(Point p, Color c) {}record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

如果要提取左上角点的颜色,我们可以这样写:EkT28资讯网——每日最新资讯28at.com

Object r = new Rectangle(    new ColoredPoint(new Point(0, 0), Color.RED),     new ColoredPoint(new Point(100, 100), Color.BLUE)  ) ;// 从java 21起  if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) {  System.out.printf("%s, %s%n", ul, lr) ;}

输出结果EkT28资讯网——每日最新资讯28at.com

ColoredPoint[p=Point[x=0, y=0], c=RED], ColoredPoint[p=Point[x=100, y=100], c=BLUE]

如果你希望访问具体的颜色值,record模式还支持嵌套,如下示例:EkT28资讯网——每日最新资讯28at.com

// 从java 21起if (r instanceof Rectangle(    ColoredPoint(Point(int x, int y), Color c1),     ColoredPoint lr  )) {  System.out.printf("x = %d, y = %d%n", x, y) ;}

1.4 嵌套模式无法匹配情况

在下面这情况下是无法进行匹配的EkT28资讯网——每日最新资讯28at.com

public record Pair(Object x, Object y) {}Pair p = new Pair(42, 42);if (p instanceof Pair(String s, String t)) {  System.out.println(s + ", " + t);} else {  System.out.println("Not a pair of strings") ;}

以上是关于record 模式的所有内容。EkT28资讯网——每日最新资讯28at.com

2. switch模式匹配

该功能最初由 JEP 406(JDK 17)提出,后经 JEP 420(JDK 18)、427(JDK 19)和 433(JDK 20)改进。它与 "1. record模式 "功能(JEP 440)共同发展。EkT28资讯网——每日最新资讯28at.com

先来看下如下这段代码EkT28资讯网——每日最新资讯28at.com

Object obj = 100L ;if (obj instanceof Integer) {  Integer i = (Integer) obj ;  obj = String.format("int %d", i);} else if (obj instanceof Long) {  Long l = (Long) obj ;  obj = String.format("long %d", l);} else if (obj instanceof String) {  String s = (String) obj ;  obj = String.format("String %s", s);}

有个instanceof 模式以后就可以简化这样了EkT28资讯网——每日最新资讯28at.com

Object obj = 100L ;if (obj instanceof Integer i) {  obj = String.format("int %d", i);} else if (obj instanceof Long l) {  obj = String.format("long %d", l);} else if (obj instanceof String s) {  obj = String.format("String %s", s);}System.out.printf("result obj = %s%n", obj) ;

注意:上面的代码有2个问题EkT28资讯网——每日最新资讯28at.com

  1. 上面的代码有如果没有编译器的作用,那么它的时间复杂度将是O(n)
  2. 隐藏了一个BUG,当if,else没有判断到某个类型时可能会出现问题上面的代码并没有else,因为不强制所以当判断遗漏了某种类型时可能会给程序带来潜在的问题。

从Java 21开始,我们可以如下处理上面的if.. else EkT28资讯网——每日最新资讯28at.com

var ret = switch (obj) {  case Integer i -> String.format("int %d", i);  case Long l    -> String.format("long %d", l);  case String s  -> String.format("String %s", s);  default        -> obj.toString() ;};System.out.printf("result ret = %s%n", ret) ;

在过去我们知道如果switch的每个case没有break或者return,那么它会穿透到下一个case直到遇到break或return。并且在传统的switch中没有default也是可以的。但是在上面的代码中必须要有default子句。EkT28资讯网——每日最新资讯28at.com

2.1 switch与null值

传统上,如果switch表达式值为空,switch 语句和表达式会抛出 NullPointerException,因此必须在 switch 之外进行空判断:EkT28资讯网——每日最新资讯28at.com

String s = null ;switch (s) {  // 如果不清楚这里的语法,你应该先看看java14对switch新语法的介绍  case "a", "b" -> System.out.println("a or b") ;  default -> System.out.println("defualt value") ;}

控制台输出EkT28资讯网——每日最新资讯28at.com

图片图片EkT28资讯网——每日最新资讯28at.com

在上面的代码中在过去,我们要先对s进行null的判断,再进行switch,否则有可能就会出现上面的错误。修改如下:EkT28资讯网——每日最新资讯28at.com

if (s == null) {  return ;}switch (s) {  // TODO}

以上代码是Java 21之前,从Java 21起,我们可以如下:EkT28资讯网——每日最新资讯28at.com

switch (s) {  case null -> System.out.println("oops") ;  case "a", "b" -> System.out.println("a or b") ;  default -> System.out.println("defualt value") ;}

无需单独的if判断是否为null情况。EkT28资讯网——每日最新资讯28at.com

2.2 switch条件判断

在case中还可以添加if...else判断EkT28资讯网——每日最新资讯28at.com

static void fn1(String resp) {  switch (resp) {    case String s -> {      if (s.equalsIgnoreCase("success"))        System.out.println("处理成功");      else if (s.equalsIgnoreCase("failure"))        System.err.println("处理失败");      else        System.out.println("未知结果") ;    }  }}

在case中是使用when子句EkT28资讯网——每日最新资讯28at.com

static void fn2(String resp) {  switch (resp) {    case null -> {}    case String s     when s.equalsIgnoreCase("success") -> {      System.out.println("处理成功");    }    case String s    when s.equalsIgnoreCase("failure") -> {      System.err.println("处理失败");    }    case String s -> {        System.out.println("未知结果") ;    }  }}

这样,switch的可读性就更强了。EkT28资讯网——每日最新资讯28at.com

2.3 switch与enum常量

在Java 21之前,switch的case表达式必须是枚举类型,标签必须是枚举常量的简单名称,如下示例:EkT28资讯网——每日最新资讯28at.com

public enum Color { RED, BLUE, GREEN }public static void fn1(Color c) {  switch (c) {    case RED, BLUE -> System.out.println("我喜欢的颜色") ;    case GREEN -> {      // TODO    }    default -> System.out.println("我讨厌的颜色") ;  }}

上面说的标签必须是枚举常量的简单名称什么意思呢?就是说在java21之前使用枚举时的标签不能是下面这种写法:EkT28资讯网——每日最新资讯28at.com

case Color.GREEN -> {}

而从Java 21起可以使用这种语法。EkT28资讯网——每日最新资讯28at.com

3. 虚拟线程

关于虚拟线程请查看这篇文章:EkT28资讯网——每日最新资讯28at.com

【技术革命】JDK21虚拟线程来袭,让系统的吞吐量翻倍!EkT28资讯网——每日最新资讯28at.com

4. 字符串模版

注:这是一个预览功能EkT28资讯网——每日最新资讯28at.com

EkT28资讯网——每日最新资讯28at.com

编译:javac --enable-preview --source 21 -Xlint:preview Xxx.javaEkT28资讯网——每日最新资讯28at.com

运行:java --enable-preview XxxEkT28资讯网——每日最新资讯28at.com

EkT28资讯网——每日最新资讯28at.com

在开发中字符串相关的操作是非常非常多的,虽然Java 提供了多种字符串组成机制,但遗憾的是,所有机制都有缺点。EkT28资讯网——每日最新资讯28at.com

  • 使用+操作符拼接字符串,看着都不好理解
String result = x + " + " + y + " = " + (x + y) ;
  • 冗余的StringBuilder
String s = new StringBuilder().append(x).append(" + ")  .append(y).append(" = ").append(x + y).toString() ;
  • String#format 与 String#formatted将格式字符串与参数分离,避免了类型错配:
int x = 10, y = 20 ;String s = String.format("%2$d + %1$d = %3$d", x, y, x + y);String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y) ;
  • java.text.MessageFormat要求太多,而且格式字符串中使用了不熟悉的语法:
String ret = MessageFormat.format("{0} + {1} = {2}", x, y, x + y) ;

4.1 STR 模板处理器

STR 是 Java 平台定义的模板处理器。它通过用表达式的(字符串化)值替换模板中的每个嵌入表达式来执行字符串插值。EkT28资讯网——每日最新资讯28at.com

String firstName = "Bill" ;String lastName  = "Duck" ;String fullName  = STR."/{firstName} /{lastName}" ;System.out.println(fullName) ;

输出结果EkT28资讯网——每日最新资讯28at.com

Bill Duck

注:STR 是一个公共静态最终字段,会自动导入到每个 Java 源文件中。EkT28资讯网——每日最新资讯28at.com

表达式还可以执行相应的操作,如下:EkT28资讯网——每日最新资讯28at.com

int x = 10, y = 20 ;String result = STR."/{x} + /{y} = /{x + y}" ;System.out.println(result) ;// 10 + 20 = 30

表达式中还可以调用方法EkT28资讯网——每日最新资讯28at.com

static String getName() {  return "张三" ;}static record Req(String date, String time) {}static void fn5() {  String s = STR."我的名字是 /{getName()} ";  System.out.println(s) ;  Req req = new Req("2000-01-01", "23:59:59") ;  String t = STR."Access at /{req.date} /{req.time}";  System.out.println(t) ;}

输出结果EkT28资讯网——每日最新资讯28at.com

我的名字是 张三Access at 2000-01-01 23:59:59

多行模版字符串EkT28资讯网——每日最新资讯28at.com

static void fn6() {  String name    = "张三";  String phone   = "1899999999";  String address = "xxxooo";  String json = STR."""  {    "name":    "/{name}",    "phone":   "/{phone}",    "address": "/{address}"  }  """;  System.out.println(json);}

输出结果EkT28资讯网——每日最新资讯28at.com

{   "name": "张三",   "phone": "1899999999",   "address": "xxxooo"}

以上是基于STR模版处理器的内容,接下来介绍另外一个。EkT28资讯网——每日最新资讯28at.com

4.2 FMT 模板处理器

FMT 是 Java 平台定义的另一种模板处理器。FMT 与 STR 类似,它执行插值,但也解释嵌入式表达式左侧的格式规范。格式说明符与 java.util.Formatter 中定义的格式说明符相同。EkT28资讯网——每日最新资讯28at.com

record Rectangle(String name, double width, double height) {  double area() {    return width * height;  }}public static void main(String[] args) {  Rectangle[] zone = new Rectangle[] {    new Rectangle("Alfa", 17.8, 31.4),    new Rectangle("Bravo", 9.6, 12.4),  };  String s = FMT."""    Description     Width    Height     Area    %-12s/{zone[0].name}  %7.2f/{zone[0].width}  %7.2f/{zone[0].height}     %7.2f/{zone[0].area()}    %-12s/{zone[1].name}  %7.2f/{zone[1].width}  %7.2f/{zone[1].height}     %7.2f/{zone[1].area()}    /{" ".repeat(28)} Total %7.2f/{zone[0].area() + zone[1].area() + zone[2].area()}  """;  System.out.println(s) ;}

5. 序列集合

在Java21 之前的集合类中要获取第一个和最后一个元素,不同的集合操作方式不同或者压根就没有对应的方法。如下示例:EkT28资讯网——每日最新资讯28at.com

图片图片EkT28资讯网——每日最新资讯28at.com

在说遍历集合,正向时(从第一个到最后一个)操作方法基本一致。但是反向时遍历时每个集合就又不相同了。EkT28资讯网——每日最新资讯28at.com

在JDK21中提供了如下3个序列接口:EkT28资讯网——每日最新资讯28at.com

  • SequencedCollection
public interface SequencedCollection<E> extends Collection<E> {  SequencedCollection<E> reversed() ;  default void addFirst(E e) ;  default void addLast(E e) ;  default E getFirst() ;  default E getLast() ;  default E removeFirst() ;  default E removeLast() ;}
  • SequencedSet
public interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {  SequencedSet<E> reversed();}
  • SequencedMap
public interface SequencedMap<K, V> extends Map<K, V> {  SequencedMap<K, V> reversed() ;  default Map.Entry<K,V> firstEntry() ;  default Map.Entry<K,V> lastEntry() ;  default Map.Entry<K,V> pollFirstEntry() ;  default Map.Entry<K,V> pollLastEntry() ;  default V putFirst(K k, V v) ;  default V putLast(K k, V v) ;  // other}

以上3个集合都提供了对应的获取第一个和最后一个元素的方法及集合反转方法。上面定义的三个新接口与现有的集合类型层次结构非常吻合,如下图:EkT28资讯网——每日最新资讯28at.com

图片图片EkT28资讯网——每日最新资讯28at.com

对现有的类和接口进行了如下调整:EkT28资讯网——每日最新资讯28at.com

  • List 现在将 SequencedCollection 作为其直接超接口、
  • Deque 现在将 SequencedCollection 作为其直接超接口、
  • LinkedHashSet 进一步实现了 SequencedSet、
  • SortedSet 现在将 SequencedSet 作为其直接超接口、
  • LinkedHashMap 进一步实现了 SequencedMap,而
  • SortedMap 现在将 SequencedMap 作为其直接超接口。

6. 未命名模式&变量

注:这是一个预览功能EkT28资讯网——每日最新资讯28at.com

先看下面这个示例EkT28资讯网——每日最新资讯28at.com

public record Point(int x, int y) {}enum Color { RED, GREEN, BLUE }record ColoredPoint(Point p, Color c) {}record Rectangle(ColoredPoint cp) {}  Object obj = new Rectangle(    new ColoredPoint(new Point(10, 10), Color.RED)  ) ;if (obj instanceof Rectangle(ColoredPoint(Point(int x, int y), Color c))) {  System.out.printf("x = %d, y = %d%n", x, y) ;}

在上面的if判断中,对于Color c变量并没有使用,从Java 21开始我们可以像下面这样改写:EkT28资讯网——每日最新资讯28at.com

if (obj instanceof Rectangle(ColoredPoint(Point(int x, int y), _))) {  System.out.printf("x = %d, y = %d%n", x, y) ;}

使用一个 "_" 下划线代替即可。EkT28资讯网——每日最新资讯28at.com

未使用的变量EkT28资讯网——每日最新资讯28at.com

int[] arr = {1, 2, 3, 4, 5} ;int total = 0 ;for (var a : arr) {  total++ ;}

在这个示例中,变量a并没有使用,所以从Java 21开始可以改写如下:EkT28资讯网——每日最新资讯28at.com

for (var _ : arr) {  total++ ;}

对于这样没有使用的变量,我们可以用一个 "_" 下划线代替。其它示例:EkT28资讯网——每日最新资讯28at.com

try {  int a = 1 / 0 ;} catch (Exception _) { // 这里没有用到异常通过可以使用 _}

注:我用的Eclipse没法直接使用,我这里是通过记事本编写,通过命令行编译&运行。EkT28资讯网——每日最新资讯28at.com

7. 未命名的类&Main方法EkT28资讯网——每日最新资讯28at.com

注:这是一个预览功能EkT28资讯网——每日最新资讯28at.com

下面这个代码是学习java的入门代码EkT28资讯网——每日最新资讯28at.com

public class UnnamedClassAndMain { public static void main(String[] args) {   System.out.println("Hello World!!!") ; }}

从Java 21开始,我们可以简化成如下形式了EkT28资讯网——每日最新资讯28at.com

public class UnnamedClassAndMain {  void main() {    System.out.println("Hello World!!!") ;  }}

未命名的类EkT28资讯网——每日最新资讯28at.com

还是拿上面的程序演示,我们还可以继续简化如下形式:EkT28资讯网——每日最新资讯28at.com

void main() {  System.out.println("Hello World!!!") ;}

对,文件中只有一个极简的方法,连类的声明都没有了。你甚至还可以如下,定义方法,方法调用EkT28资讯网——每日最新资讯28at.com

String name = "Pack" ;String getName() {  return name ;}void main() {  System.out.println(getName()) ;}

类文件直接定义方法,声明变量。EkT28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-88373-0.html要不要升级?Java 21强大的新特性,代码量减半

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Python进阶:掌握15条PEP 8黄金法则

下一篇: Go 事件驱动编程:实现一个简单的事件总线

标签:
  • 热门焦点
Top