Java 14 开箱,新特性Record、instanceof、switch香香香香

沉默王二Java核心Java新特性约 2117 字大约 7 分钟

10.4 Java 14 新特性

Java 14 的时候,新增了记录 Record、模式匹配 instanceof 等新特性,转正了 Java 12 时的 switch 表达式,我们一起来过一遍。

01、下载 JDK 14

截止到2023年03月30日,Java 20 已经发布了,不过不是 LTS(长期支持)版本,Java 17、11、8 是目前提供支持的 LTS 版本。

要想开箱,得先下载 JDK 14(如果你有更高版本,当然也可以),不然拿什么开箱呢,对吧?有 2 处地方可供下载,Oracle 上可以下载商用版, jdk.java.netopen in new window 上可以下载开源版。我们就选择后者吧。

我目前用的是 Windows 操作系统,所以就选择 Windows 版的 zip 包进行下载,完成后记得解压。

02、升级 IntelliJ IDEA

截止到 2023年03月30日,Intellij IDEA 的最新版本是 2023.1。

需要把 IDEA 升级到抢先体验版 2020.1 EAP(如果你当前使用的版本大于此,当然也可以),否则无法支持 Java 14 的新特性。

社区版的下载地址如下所示:

[https://www.jetbrains.com/idea/nextversion/#section=windows](https://www.jetbrains.com/idea/nextversion/#section=windows)

安装的时候可以把之前的版本卸载,也可以选择保留。完成后,我们来新建一个 Java 14 的项目。

01、instanceof

按照新特性的顺序,我们就先从 instanceof 说起吧。旧式的 instanceof 的用法如下所示:

public class OldInstanceOf {
    public static void main(String[] args) {
        Object str = "Java 14,真香";
        if (str instanceof String) {
            String s = (String)str;
            System.out.println(s.length());
        }
    }
}

需要先使用 instanceof 在 if 条件中判断 str 的类型是否为 String(第一步),再在 if 语句中将 str 强转为字符串类型(第二步),并且要重新声明一个变量用于强转后的赋值(第三步)。

三个步骤也不算多,但总觉得应该还有更好的语法,这不,Java 14 就想到了这一层。

public class NewInstanceOf {
    public static void main(String[] args) {
        Object str = "Java 14,真香";
        if (str instanceof String s) {
            System.out.println(s.length());
        }
    }
}

可以直接在 if 条件判断类型的时候添加一个变量,就不需要再强转和声明新的变量了。是不是特别简洁?但模式匹配的 instanceof 在 Java 14 中是预览版的,默认是不启用的,所以这段代码会有一个奇怪的编译错误(Java 14 中不支持模式匹配的 instanceof)。

那怎么解决这个问题呢?需要在项目配置中手动设置一下语言的版本。

设置完成后,编译错误就随风飘走了。程序输出的结果如下所示:

10

不错不错,真香。想知道 Java 编译器在背后帮我们做了什么吗?看一下反编译后的字节码就明白了。

public class NewInstanceOf {
    public NewInstanceOf() {
    }

    public static void main(String[] args) {
        Object str = "Java 14,真香";
        String s;
        if (str instanceof String && (s = (String)str) == (String)str) {
            System.out.println(s.length());
        }

    }
}

在 if 条件判断前,先声明了变量 s,然后在 if 条件中进行了强转 s = (String)str),并且判断了 s 和 str 是否相等。确实是一个解放开放者生产力的好特性,强烈希望这个特性在下个版本中转正。

02、Records

在之前的一篇文章中,我谈到了类的不可变性open in new window,它是这样定义的:

public final class Writer {
    private final String name;
    private final int age;

    public Writer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

那么,对于 Records 来说,一条 Record 就代表一个不变的状态。尽管它会提供诸如 equals()hashCode()toString()、构造方法,以及字段的 getter,但它无意替代可变对象的类(没有 setter),以及 Lombok 提供的功能。

来用 Records 替代一下上面这个 Writer 类:

public record Writer(String name, int age) { }

你看,一行代码就搞定。关键是比之前的代码功能更丰富,来看一下反编译后的字节码:

public final class Writer extends java.lang.Record {
    private final java.lang.String name;
    private final int age;

    public Writer(java.lang.String name, int age) { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public java.lang.String name() { /* compiled code */ }

    public int age() { /* compiled code */ }
}

类是 final 的,字段是 private final 的,构造方法有两个参数,toString()hashCode()equals() 方法也有了,getter 方法也有了,只不过没有 get 前缀。但是没有 setter 方法,也就是说 Records 确实针对的是不可变对象——鉴定完毕。那怎么使用 Records 呢?

public class WriterDemo {
    public static void main(String[] args) {
        Writer writer = new Writer("沉默王二",18);
        System.out.println("toString:" + writer);
        System.out.println("hashCode:" + writer.hashCode());
        System.out.println("name:" + writer.name());
        System.out.println("age:" + writer.age());

        Writer writer1 = new Writer("沉默王二", 18);
        System.out.println("equals:" + (writer.equals(writer1)));
    }
}

程序输出的结果如下所示:

toString:Writer[name=沉默王二, age=18]
hashCode:1130697218
name:沉默王二
age:18
equals:true

不错不错,真香,以后定义不可变类时就简单了,强烈希望这个特性在下个版本中转正。

03、switch 表达式

关于 switch 表达式,我在之前的一篇文章中已经详细说明了,点击传送门open in new window可以跳转过去看看。两周时间过去了,switch 表达式终于“媳妇熬成婆”,转正了,恭喜恭喜。

记得这篇文章发表到掘金的时候,被喷子各种无脑 diss,说:“还以为你有什么技巧,没想到用的是 Java 13,可我们还停留在 Java 8 啊!”这显然是一种固步自封的心态,非常不可取,程序员不应该这样。一个最简单的道理就是,Java 6 当年也很经典,不是被 Java 8 取代了吗?随着时间的推移,Java 8 早晚会被更划时代的新版本取代——总要进步嘛。

关于 switch 表达式,这里就简单地搬个例子给你瞧瞧:

public class SwitchDemo {
    enum PlayerTypes {
        TENNIS,
        FOOTBALL,
        BASKETBALL,
        PINGPANG,
        UNKNOWN
    }

    public static void main(String[] args) {
        System.out.println(createPlayer(PlayerTypes.BASKETBALL));
    }

    private static String createPlayer(PlayerTypes playerType) {
        return switch (playerType) {
            case TENNIS -> "网球运动员费德勒";
            case FOOTBALL -> "足球运动员C罗";
            case BASKETBALL -> "篮球运动员詹姆斯";
            case PINGPANG -> "乒乓球运动员马龙";
            case UNKNOWN -> throw new IllegalArgumentException("未知");
        };
    }
}

除了可以使用 -> 的新式语法,还可以作为 return 结果,真香。

04、Text Blocks

在文本块(Text Blocks)出现之前,如果我们需要拼接多行的字符串,就需要很多英文双引号和加号,看起来就好像老太婆的裹脚布,非常不雅。如果恰好要拼接一些 HTML 格式的文本(原生 SQL 也是如此)的话,还要通过空格进行排版,通过换行转义符 \n 进行换行,这些繁琐的工作对于一名开发人员来说,简直就是灾难。

public class OldTextBlock {
    public static void main(String[] args) {
        String html = "<html>\n" +
                "    <body>\n" +
                "        <p>Hello, world</p>\n" +
                "    </body>\n" +
                "</html>\n";
        System.out.println(html);
    }
}

Java 14 就完全不同了:

public class NewTextBlock {
    public static void main(String[] args) {
        String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;
        System.out.println(html);
    }
}

多余的英文双引号、加号、换行转义符,统统不见了。仅仅是通过前后三个英文双引号就实现了。我只能说,香,它真的香!


GitHub 上标星 7600+ 的开源知识库《二哥的 Java 进阶之路open in new window》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,可以说是通俗易懂、风趣幽默……详情戳:太赞了,GitHub 上标星 7600+ 的 Java 教程open in new window

微信搜 沉默王二 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 222 即可免费领取。