Java14中的好用特性分享(1)

发表于2020-05-06,长度4054, 887个单词, 11分钟读完
Flag Counter

Java 新的版本发布规划的结果就是Java更新似乎快得让人眼花缭乱,之前Java小版本更新没人在乎,现在没有了小版本,即使是非长期支持版发布也会惊起一滩鸥鹭。Java14就是非长期支持版,下一版LTS要等到2021年9月份的Java17。不过对于Java9以后就不熟悉的开发者来说,目前的jdk提供的很多新语法和新API的确非常诱人。这里我选择了一些Java14中的好用的特性简单说一下。

Java 目前的发布计划有点像Ubuntu,每年3月和9月都会出一版。但是多数是非长期支持版(non-LTS),长期支持版(LTS)要三年才发一次。第一个LTS是2018年9月的Java11(所以Java9和10也是nlts),所以下一版就是2021年了。

Java 14虽然提供了很多新特性(大家可以随便百度一下,比如《Java14发布,16大新特性,代码更加简洁明快》号称16大新特性,一般认为最少有12大新特性),但是对于我哦们平常开发用到的不多,主要有5条:

  • switch 表达式
  • instanceof的模式匹配
  • 更有用的空指针异常信息
  • record 类型
  • 文本块

而且更要命的是,这里面只有两条是标准功能:switch表达式和空指针异常。其余都是预览功能,也就是后面的版本不算可能转正也可能移除。不过这么好用的功能不转正没有天理啊!

更有用的空指针异常信息

先介绍这个吧,我觉得是最不可代替的功能。其他功能我们都可以通过自己编码解决,这个只能靠JDK提供。

以前的Java在报空指针的时候只会给出行号,但是行内到底是哪个变量空指针了依然让人一览懵X。比如下面的代码:

public class Obj {
    private Obj2 o2;
}

public class Obj2 {
    private String name;
}

public static void main(String[] args) {
    Obj o = new Obj();
    System.out.println(o.getO2().getName());
}

异常信息是

Exception in thread "main" java.lang.NullPointerException
	at info.manxi.Main.main(Main.java:15)

那么到底是o是空指针还是o2是空指针(当然这里是明确的),还是println方法不能接受null参数(小白可能不知道),异常并没有指明。

Java14中提供了更有用的异常信息,不过这个功能默认是关闭的,需要通过-XX:+ShowCodeDetailsInExceptionMessages参数打开:

再次运行,异常信息变为

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "info.manxi.Obj2.getName()" because the return value of "info.manxi.Obj.getO2()" is null
	at info.manxi.Main.main(Main.java:15)

明确指出来o2是空。

更好用的switch表达式

Java中的switch语法经过了多次优化。大的来讲分两类,之前叫switch语句,现在叫switch表达式。语句和表达式的区别是语句可以赋值给一个变量,整体是表达式。

最早的switch语句只能匹配4种基本数据类型和枚举,后来通过语法糖实现了可以匹配String类型。Java12开始预览提供赋值功能。

假设我们需要通过case判断来确定一个外部变量的新值,我们可能需要这样做:

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    String b = "";
    switch (a) {
        case 0: {
            b = "0";
            break;
        }
        case 1: {
            b = "12";
            break;
        }
        case 2: {
            b = "987";
            break;
        }
        default:{

        }
    }
    System.out.println(b);
}

上面这段代码在IDEA中会得到提示:使用增强版switch

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    String b = "";
    switch (a) {
        case 0 -> b = "0";
        case 1 -> b = "12";
        case 2 -> b = "987";
        default -> {

        }
    }
    System.out.println(b);
}

IDEA 帮我们用->代替了冒号,然后去掉了break,因为即使没有break也不会再进入下一个case块了。

进一步,由于default没有处理b,我们把b的初始化值放在default块,这样可以把整个switch块复制给b(末尾加分号):

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    String b =
            switch (a) {
                case 0 -> b = "0";
                case 1 -> b = "12";
                case 2 -> b = "987";
                default -> b = "";
            };
    System.out.println(b);
}

实际上,case中的b = 根本没用,直接返回字符串就可以,返回一个赋值操作看起来不伦不类:

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    String b = switch (a) {
        case 0 -> "0";
        case 1 -> "12";
        case 2 -> "987";
        default -> "";
    };
    System.out.println(b);
}

看起来似乎不错,不过到现在其实并没有接触到switch表达式的核心:switch表达式的核心是yield。可能有人熟悉其他语言(比如python)见过yield关键字,不过yield和java9引入模块后的exports、modules等一样,并非保留字,可以用做变量名:

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    int yield = Integer.parseInt(args[1]);
    String b = switch (a) {
        case 0 -> "0";
        case 1 -> "12";
        case 2 -> {
            if (yield > 0) {
                yield "---";
            }
            yield "32333";
        }
        default -> "";
    };
    System.out.println(b);
}

yield的作用就是case中的return,用于在switch表达式中根据条件或者其他逻辑判断等生成返回值。

习惯老写法的我们,可以通过生成一个新方法和return来实现同样的功能

使用switch表达式,必须把所有可能情况都覆盖完,不然编译不了。通常会使用default

最后说一下多case合并匹配。以前使用switch语句的时候,因为没有break会直接进入下个case块,所以多case合并匹配同一个结果的时候只要把几个case并列起来就行。使用switch表达式就不行了,如果要合并,就连case也去掉,只留第一个case:

public static void main(String[] args) {
    int a = Integer.parseInt(args[0]);
    int yield = Integer.parseInt(args[1]);
    String b = switch (a) {
        case 0, 1, 2 -> {
            if (yield > 0) {
                yield "---";
            }
            yield "32333";
        }
        default -> "";
    };
    System.out.println(b);
}
Written on May 6, 2020
分类: dev, 标签: java
如果你喜欢,请赞赏! davelet