0%

Java语言新特性漫谈:Java 15篇

文本块(正式版)

历经 Java 13 和 Java 14 两个预览版,文本块特性在 Java 15 终于转正。

有了文本块,就可以很容易地使用多行文本,而不用过多地考虑转义字符换行等等问题。并且文本块会以可预期的方式对文本进行格式化,同时也可以人为控制格式化。

可以分别参考《Java语言新特性漫谈:Java 13篇》《Java语言新特性漫谈:Java 14篇》相关特性章节。

instanceof操作符的模式匹配(预览特性第2版)

Java 14 版中该预览特性的再次预览版,但是本次预览没有任何修改。

可参考《Java语言新特性漫谈:Java 14篇》相关特性章节。

记录类(预览特性第2版)

自定义构造器

我们可以自定义记录类的规范构造器(Canonical Constructor):

1
2
3
4
5
6
7
8
9
10
record Rectangle(double length, double width) {
public Rectangle(double length, double width) {
if (length <= 0 || width <= 0) {
throw new java.lang.IllegalArgumentException(
String.format("Invalid dimensions: %f, %f", length, width));
}
this.length = length;
this.width = width;
}
}

如果仅仅是为了做校验,使用 Compact 构造器就足够了。
可参考《Java语言新特性漫谈:Java 14篇》相关特性章节。

局部记录类

记录类的另一项改进是,可以在方法内声明,作为局部类。通常,这种局部记录类被声明在紧临使用代码之前,使得代码有更好的可读性。

需要注意的是,局部记录类是隐式 static 的,因此,它不能访问外围方法的任何变量。

这与普通局部类不同,普通局部类不可能是 static 的。

序列化

记录类支持序列化与反序列化。特别的是,不能重写记录类序列化与反序列化相关的方法。

记录类的公共父类是 java.lang.Record,其实现了 Serializable 接口,所以是可序列化的。
从经验上讲,序列化不算特别常用,这里不过多赘述。更多内容可参考 Oracle 相关文档《Serializable Records》《Serialization of Records》

密封类(Sealed Class,预览特性)

这貌似是个从 Kotlin 等语言“借鉴”来的特性!
其实,Java 早期就有“密封包(sealed package)”的概念,用以保证类版本的一致性。简单来说,在打 jar 包时,可以在 Manifest.mf 文件中指定某个包是否密封。如果是,则该包下的所有类必须在同一个 jar 文件(即当前 jar 文件)中,否则将导致 SecurityException

密封类,限制了其他类对它的继承。

声明语法

密封类声明只需要在名称前增加 sealed 修饰符,并在之后追加 permits 子句指定允许的子类即可。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public sealed class Shape
permits Circle, Square, Rectangle {
}
// 1. final 修饰
public final class Circle extends Shape {
public float radius;
}
// 2. non-sealed 修饰
public non-sealed class Square extends Shape {
public double side;
}
// 3. sealed 修饰
public sealed class Rectangle extends Shape permits FilledRectangle {
public double length, width;
}

public final class FilledRectangle extends Rectangle {
public int red, green, blue;
}

注意,密封类 permits 子句是可省略的,前提是子类都声明在当前文件中。

允许子类的限制

密封类允许的子类有以下限制:

  • 对于密封类而言,所有允许的子类在编译时都必须是可访问的
  • 允许的子类必须直接派生自密封类
  • 允许的子类修饰符必有 finalsealednon-sealed 三者之一(可参考上文示例)
  • 允许的子类应与密封类在同一个模块(如果密封类在一个命名模块中)或在同一个包(如果密封类在未命名模块中)

TODO:最后一点有待研究……

声明密封接口

密封接口与密封类大体相同,区别在于:

  • 密封接口的 permits 子句不仅可以指定允许的实现类,也可以指定允许的子接口
  • 子接口修饰符只能是 sealednon-sealed(显然接口不可能用 final 修饰)

记录类作为允许子类

记录类(Record)可以作为密封类允许的子类。特别之处在于,记录类是隐式 final 的,因此,不用finalsealednon-sealed 修饰符。

API更新

Class 类新增了两个与密封类相关的方法:ClassDesc[] permittedSubclasses() 方法返回密封类的所有允许子类,否则返回空数组;boolean isSealed() 方法判断一个类/接口是否密封。

参考

Oracle doc - Programmer’s Guide to Text Blocks