JDK17都出来了,你还在用JDK8?
JDK17的一些新特性
本文旨在介绍一些JDK17的新特性,同时限于篇幅,本文仅列举其中一些差异,而不是全部差异;
PS: JDK17是Java的一个LTS版本(长期支持版本),可以放心迁移过来;
instanceof
JDK8中的语法:
Object o = something;
if (o instanceof String) {
    String str = (String)o;
    // do something
}
在JDK17中我们可以这样写:
Object o = something;
if (o instanceof String str) {
    // do something, 注意上边String后边跟了一个str,相当于把o强转为String同时使用变量名str引用,我们后续可以直接使用str而不用再加一层强转了
}
switch
普通switch
如果switch中只有一条语句时,JDK8中的语法:
String str = something;
switch (str) {
    case "123":
        System.out.println(str);
        break;
    case "456":
        System.out.println(str);
        break;
}
可以看出每个case后边都需要一个 break ,否则会穿透到后边的 case 语句;
JDK17中的语法:
String str = something;
switch (str) {
    case "123" -> System.out.println(str);
    case "456" -> System.out.println(str);
    default -> System.out.println(str);
}
switch的模式匹配
JDK8中的语法:
Object o = something;
if (o instanceof String) {
    String str = (String)o;
    // do something
} else if (o instanceof Integer) {
    Integer integer = (Integer)o;
    // do something
}
在JDK17中我们可以这样写(case语句也可以展开,这里为了省事就用了这种写法):
Object o = something;
switch (o) {
    case Integer i -> System.out.println(i);
    case String str -> System.out.println(str);
    default -> System.out.println(o);
}
null值处理
传统(JDK8)switch语句需要提前判null,否则会抛出NPE:
String str = something;
if (str == null) {
    // do something
    return;
}
switch (str) {
    case "123":
        System.out.println(str);
        break;
    case "456":
        System.out.println(str);
        break;
}
JDK17中无需判空,可以直接 case null :
String str = something;
switch (str) {
    case null -> System.out.println("null");
    case "123" -> System.out.println(str);
    case "456" -> System.out.println(str);
    default -> System.out.println(str);
}
复杂条件的case优化:
对于以下代码:
class Shape {}
class Rectangle extends Shape {}
class Triangle  extends Shape { int calculateArea() { ... } }
static void testTriangle(Shape s) {
    switch (s) {
        case null:
            break;
        case Triangle t:
            if (t.calculateArea() > 100) {
                System.out.println("Large triangle");
                break;
            }
        default:
            System.out.println("A shape, possibly a small triangle");
    }
}
可以优化为:
class Shape {}
class Rectangle extends Shape {}
class Triangle  extends Shape { int calculateArea() { ... } }
static void testTriangle(Shape s) {
    switch (s) {
        case Triangle t && (t.calculateArea() > 100) ->
            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    }
}
密封类(sealed Class)
在JDK8中,如果我们一个类只想要只想要指定的子类(我们自己编写的类)实现,而不希望其他依赖方自己实现(这种场景在框架编写中很常见),我们通常的方法是将构造器设置为私有的,然后使用 静态内部类来继承该类,或者将构造器设置为package访问级别的,然后在同一个包中编写类来继承;这两种方式虽然都能实现我们的目标(在某些场景也能被打破,例如用户自己编写了一个同名包,然 后就能在包中继续继承该类了),但是都不是太优雅,在我们升级JDK17后该问题也将被解决,JDK17中引入了一个新的关键字 sealed 用来修饰我们不想要其他人私自继承的类(或者接口),然后使用 permits 关键字指定都有那些类可以继承本类,用法如下:
public abstract sealed class TestA permits TestB {
    
}
public class TestB extends TestA {
    
}
这里我们使用 sealed 关键字声明 TestA 不能随便被其他类继承,然后使用 permits 声明只有 TestB 才能继承 TestA ,注意,这里 TestB 和 TestA 必须在同一个包中,如 果不在同一个包中,那么 permits 关键字后的 TestB 必须带包名(例如com.JoeKerouac.TestB);
联系我
作者微信:JoeKerouac
微信公众号(文章会第一时间更新到公众号):代码深度研究院
GitHub:https://github.com/JoeKerouac
参考文献
jdk17完整发布变更说明文档(注:截止文章发布时该地址仍有效,后续JDK继续升级,JDK17被归档后该地址可能无效):https://jdk.java.net/17/release-notes
