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 ...
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM