此外,Oracle 官方也有清单可供一览: Java Language Changes (opens new window)
当 Java 8 引入流和 Lambda 这两个重大更新时,函数式编程风格赋予了 Java 更少模板代码的语法。虽然最近的版本更新没添加这么富有影响的特性,但带来了很多较小的改进。自从 Java 切换到一个更快的发布节奏后,每六个月就会发布一个新版本。记录类可能是最近更新中最重要的一个特性,模式匹配和封闭类也会让处理纯数据更容易。
Java 17(LTS)
- switch 模式匹配(预览特性:mag:)
Java 16
Java 15
- 包含有用信息的空指针异常
Java 14
Java 11(LTS)
Java 9
- 匿名内部类的钻石操作符
- try-with-resources 语句中允许使用 effectively-final 变量
- 下划线不再是合法变量名
想要一览塑造这个新平台所有的 JEP,其涵盖了包括 API 、性能与安全方面的改进,参看这份精选清单:Java 8 以来所有的改进 (opens new window) 。
开始支持版本: JDK 17 (opens new window) ( JDK 15 (opens new window) JDK 16 (opens new window) 为预览特性)
封闭类用于限定哪些类或接口可以被用于继承或实现它们。这给设计公共 API 和替换枚举来构建固定数量的可选项,提供了一个更好的工具。
老版本的 Java 也提供了一些机制来实现类似的效果。标记为 final 的类不允许被继承,配合访问修饰符就能确保仅同一包中的类才能继承。
在此之上, 封闭类 提供了更细粒度的控制,让开发者能显式地列举其子类。
public sealed class Shape
permits Circle, Quadrilateral {...}
1
2
在这个例子中,被允许继承 Shape 类的只有 Circle 和 Quadrilateral 类。实际上, permits这个关键字有些歧义,因为它不止有允许的含义,其 要求列举的类直接继承封闭类 。
此外,正如人们所期望的那样,如果 任何其它的类试图继承这个封闭类,都会出现编译错误。
继承封闭类的类需要符合一些规则。
开发者被强制每次都需要显式定义出封闭类继承的边界,通过添加任意一个下面修饰符到被允许的子类上来实现:
final sealed non-sealed
因为子类本身也可以是封闭的,这就意味着可以定义 整条继承链包含限定的可选项 :
public sealed class Shape
permits Circle, Quadrilateral, WeirdShape {...}
public final class Circle extends Shape {...}
public sealed class Quadrilateral extends Shape
permits Rectangle, Parallelogram {...}
public final class Rectangle extends Quadrilateral {...}
public final class Parallelogram extends Quadrilateral {...}
public non-sealed class WeirdShape extends Shape {...}
1
2
3
4
5
6
7
8
9
10
11
如果这些类比较简短,且大多仅和数据相关,那么可以将它们声明在 同一个源文件中, permits 关键字也可以忽略 :
public sealed class Shape {
public final class Circle extends Shape {}
public sealed class Quadrilateral extends Shape {
public final class Rectangle extends Quadrilateral {}
public final class Parallelogram extends Quadrilateral {}
}
public non-sealed class WeirdShape extends Shape {}
}
1
2
3
4
5
6
7
8
9
10
记录类也可以作为封闭类的子类,因为它们是隐式 final 的。
被允许继承的类必须和父类(封闭类)在同一个包里,如果是使用 java 模块,那它们必须在同一模块中。
:warning:技巧:考虑使用封闭类优于枚举
在 封闭类 出现前,只能用 枚举类 对固定可选项建模,比如:
enum Expression {
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION
}
1
2
3
4
5
6
然而,所有的变量都要在同一个源文件中,且 枚举类 不支持需要实例的情况(而不是常量),例如表示一个类型的单个消息。
封闭类 提供一个比 枚举类 更好的选择,使得用普通类来为固定可选项建模成为可能。当 switch 模式匹配 在生产环境可用时就更能充分发挥其作用, 封闭类 能像枚举一样在 switch 表达式中使用,编译器能自动检查代码是否涵盖了全部情况。
枚举类的值可以使用 values 方法列举出来。对应到封闭类和封闭接口,可以使用 getPermittedSubclasses 方法例举出所有被允许继承的子类。
switch 模式匹配(预览特性:mag:)
开始支持版本: JDK 17 (opens new window) 为预览特性
此前, switch 表达式的用法十分局限:条件仅仅支持完全相等的情况,而且只支持很少几类类型:数值、枚举类和字符串。
这个预览特性增强了 swith 表达式的用法, 可以用在任意的类型上,匹配更复杂的模式 。
这些新特性是 向后兼容的 , switch 搭配传统的常量就如以往一样的使用,例如和枚举值:
var symbol = switch (expression) {
case ADDITION -> "+";
case SUBTRACTION -> "-";
case MULTIPLICATION -> "*";
case DIVISION -> "/";
};
1
2
3
4
5
6
然而,随着 JEP 394: Pattern Matching for instanceof (opens new window) 的引入,现在可以和类型模式搭配使用:
return switch (expression) {
case Addition expr -> "+";
case Subtraction expr -> "-";
case Multiplication expr -> "*";
case Division expr -> "/";
};
1
2
3
4
5
6
模式还支持 卫语句 ,写法为 type pattern && guard expression :
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
default -> "something else";
};
1
2
3
4
5
这和使用 if 声明的类型模式构成了很好的对称性,因为类似的模式可以用于条件语句:
if (o instanceof Integer i && i > 10) {
return String.format("a large Integer %d", i);
} else if (o instanceof Integer i) {
return String.format("a large Integer %d", i);
} else {
return "something else";
}
1
2
3
4
5
6
7
与 if 条件类似, 模式变量的作用域是分支敏感的 (flow sensitive)。比如,在下面的条件中,变量 i 的作用域为卫语句及其右边的表达式。
case Integer i && i > 10 -> String.format("a large Integer %d", i);
1
总体来说,模式匹配会按你期待的那样工作,但其中涉及了很多规则和边缘情况。如果你感兴趣,我推荐你读下相关的 JEPs 或是看下Pattern matching for instanceof章节。
Switch 现在也能匹配 null 值 。通常来说,当 null 值传给 switch 会报 NullPointerException 。当一个常量试图匹配 null 的时候也会出现这种情况。然而,现在可以显示得声明 null 在分支上:
switch (s) {
case null -> System.out.println("Null");
case "Foo" -> System.out.println("Foo");
default -> System.out.println("Something else");
}
1
2
3
4
5
当 switch 表达式没有完全覆盖各种情况分支,或是一个分支条件完全包含了另一个分支,编译器会报错。
Object o = 1234;
// OK
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
default -> "something else";
};
// 编译错误 - 'switch' 表达式没有涵盖所有可能的输入值
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
};
// 编译错误 - 第二个条件已包含在第一个条件分支中
String formatted = switch (o) {
case Integer i -> String.format("a small Integer %d", i);
case Integer i && i > 10 -> String.format("a large Integer %d", i);
default -> "something else";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这个 预览 特性需要通过 --enable-preview 标记来显式开启。当然我们试目以待吧,因为更多的特性将要到来: JEP405 (opens new window) 针对 Java 18 ,旨在带来可用于解构的数组模式和记录类模式。
开始支持版本: JDK 16 (opens new window) ( JDK 14 (opens new window) JDK 15 (opens new window) 为预览特性)
<
本文介绍了从 Java 9 到 17 的一系列语言特性,包括封闭类、switch 模式匹配、instanceof 模式匹配、包含有用信息的空指针异常、接口中私有方法、匿名内部类的钻石操作符以及 try-with-resources 语句的改进等。这些更新简化了代码编写,提高了代码可读性和安全性,尤其强调了模式匹配的灵活性和记录类的使用。同时,文章提醒开发者注意一些潜在问题,如缩进一致性、Windows 换行符处理等。
最低0.47元/天 解锁文章
2736

被折叠的 条评论
为什么被折叠?



