jdk17新特性

建议

本文档仅仅记录自己的一些片面认知,具体文档说明请参考官方地址: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指定的类必须具有规范名称,否则会报告编译时错误。这意味着匿名类和本地类不能成为密封类的子类型。

声明效果:

  1. 密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则属于同一个包

  2. 每个允许的子类必须为finalsealed,或non-sealed

  3. 每个允许的子类必须使用修饰符来描述它如何传播由其超类发起的密封:

    • 可以声明允许的子类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 { ... }

sealingrecord 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修饰符就可以解决这个问题,在下面这个案例中,会检测出CC的所有子类都没有实现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 ...
}
posted @ 2021-09-10 11:12  临渊不羡渔  阅读(4090)  评论(0编辑  收藏  举报