建议
本文档仅仅记录自己的一些片面认知,具体文档说明请参考官方地址:http://openjdk.java.net/projects/jdk/17/
2021.10.20补充:本文只能作为本人的学习经历,很多地方写的不是很好,推荐视频地址:jdk9-17:https://www.bilibili.com/video/BV1Fq4y1P7RQ
没必要太过执着完全深入了解,新特性全部过一遍
即可,有时间就自己写几个demo。现在intellij Idea
对于17的语法支持已经完善了,完全可以在具体使用过程中加深记忆。
Sealed修饰符
作用域:类、抽象类、接口。不可作用于内部类上
目标:声明一个类或为密封类,只有指定的类才可以继承该类。声明一个接口为密封接口,只有指定的接口可以继承该接口,只有指定的类可以实现该接口。
Sealed classes
- 子类与父类在同一包下声明方式
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square { ... }
- 子类与父类在统一模块下声明方式
package com.example.geometry;
public abstract sealed class Shape
permits com.example.polar.Circle,
com.example.quad.Rectangle,
com.example.quad.simple.Square { ... }
- 子类与父类在同一 .java 文件下声明方式
abstract sealed class Root { ...
final class A extends Root { ... }
final class B extends Root { ... }
final class C extends Root { ... }
}
声明注意:以permits
指定的类必须具有规范名称,否则会报告编译时错误。这意味着匿名类和本地类不能成为密封类的子类型。
声明效果:
-
密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则属于同一个包
-
每个允许的子类必须为
final
、sealed
,或non-sealed
。 -
每个允许的子类必须使用修饰符来描述它如何传播由其超类发起的密封:
- 可以声明允许的子类
final
以防止其在类层次结构中的部分被进一步扩展。(Record classes是隐式声明的final
。) - 一个允许的子类可以被声明
sealed
为允许它的层次结构部分比其密封超类所设想的更进一步,但以一种受限的方式。 - 可以声明一个允许的子类,
non-sealed
以便它的层次结构部分恢复为对未知子类的扩展开放。密封类不能阻止其允许的子类这样做。(修饰符non-sealed
是 为 Java 提议的第一个带连字符的关键字。)
package com.example.geometry; public abstract sealed class Shape permits Circle, Rectangle, Square, WeirdShape { ... } public final class Circle extends Shape { ... } public sealed class Rectangle extends Shape permits TransparentRectangle, FilledRectangle { ... } public final class TransparentRectangle extends Rectangle { ... } public final class FilledRectangle extends Rectangle { ... } public final class Square extends Shape { ... } public non-sealed class WeirdShape extends Shape { ... }
- 可以声明允许的子类
Sealed interfaces
sealed
修饰接口
sealed interface Celestial
permits Planet, Star, Comet { ... }
final class Planet implements Celestial { ... }
final class Star implements Celestial { ... }
final class Comet implements Celestial { ... }
sealing
与record classes
使用
package com.example.expression;
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
public record ConstantExpr(int i) implements Expr { ... }
public record PlusExpr(Expr a, Expr b) implements Expr { ... }
public record TimesExpr(Expr a, Expr b) implements Expr { ... }
public record NegExpr(Expr e) implements Expr { ... }
范围特性
该修饰符的出现是为了解决java类型范围不标准的问题,例如,如下代码是可以正常编译的,因为无法确定不会有C的子类实现了I接口。即使此处我们可以看出test方法没有意义。
interface I {}
class C {} // does not implement I
void test (C c) {
if (c instanceof I)
System.out.println("It's an I");
}
在过去,我们还可以通过使用final关键字一把切,确保不会有C的子类实现I接口。例如下面这样就会编译报错,但是这样就限制死了C类无法被继承。
interface I {}
final class C {}
void test (C c) {
if (c instanceof I) // Compile-time error!
System.out.println("It's an I");
}
而我们的sealed
修饰符就可以解决这个问题,在下面这个案例中,会检测出C
与C
的所有子类都没有实现I
,所以这里会产生编译错误
public class test {
interface I {
}
// 声明C类可由D类实现
sealed class C permits D, E {
}
// 未实现I接口
final class D extends C {
}
// 实现了I接口,实现放开,下面代码检测通过
final class E extends C /*implements I*/ {
}
void test(C c) {
// 检测出C没有子类实现I接口,如果C实现了I接口或者C的子实现了I接口,该检测通过
if (c instanceof I) {
System.out.println("It's an I");
}
}
}
再来一个案例,子类使用non-sealed
修饰符修饰。此处non-sealed
不是重点,重点是C
发现他有个子类实现了I
接口,然后以下编译可以通过
interface I {
}
sealed class C permits D, E {
}
non-sealed class D extends C {
}
final class E extends C {
}
void test(C c) {
// C存在一个不进行sealed检测的子,故该段代码检测通过
if (c instanceof I) System.out.println("It's an I");
}
Sealed修饰与类型匹配
- 旧有方式
Shape rotate(Shape shape, double angle) {
if (shape instanceof Circle) return shape;
else if (shape instanceof Rectangle) return shape;
else if (shape instanceof Square) return shape;
else throw new IncompatibleClassChangeError();
}
- 新方式
Shape rotate(Shape shape, double angle) {
return switch (shape) { // pattern matching switch
case Circle c -> c;
case Rectangle r -> shape.rotate(angle);
case Square s -> shape.rotate(angle);
// no default needed!
}
}
java类声明的语法修改如下:
NormalClassDeclaration:
{ClassModifier} class TypeIdentifier [TypeParameters]
[Superclass] [Superinterfaces] [PermittedSubclasses] ClassBody
ClassModifier:
(one of)
Annotation public protected private
abstract static sealed final non-sealed strictfp
PermittedSubclasses:
permits ClassTypeList
ClassTypeList:
ClassType {, ClassType}
反射相关
java.lang.Class添加了两个关于sealed的API
Class<?>[] getPermittedSubclasses()
boolean isSealed()
如果该类是密封的,则该方法
getPermittedSubclasses()
返回一个包含java.lang.Class
表示该类允许的子类的对象的数组 。如果类未密封,则返回一个空数组。
isSealed
如果给定的类或接口是密封的,则该方法返回 true。(比较isEnum
。)
java匹配相关语法优化
从java16开始,如果能判断对象的类型是某个类,那么不需要再进行强转,可以直接使用那个类的特性
// Old code
if (o instanceof String) {
String s = (String)o;
... use s ...
}
// New code
if (o instanceof String s) {
... use s ...
}