概述

在本教程中,我们将讨论Java SE 19中的新预览特性JEP-405:记录模式,我们将了解如何分解记录值,并将记录模式与类型模式结合使用。

2. 模型

我们将使用以下两个记录类型:一个包含经度和纬度的GPSPoint

public record GPSPoint (double latitude, double longitude) {}

以及一个包含名称和GPSPointLocation

public record Location (String name, GPSPoint gpsPoint) {}

3. 记录模式

记录模式是一种构造,它允许我们根据记录类型匹配值,并将变量绑定到记录的相应组件。我们还可以给记录模式一个可选标识符,使其成为命名记录模式,从而能够引用记录模式变量。

3.1. instanceof

Java 16引入的针对instanceof的模式匹配允许我们在instanceof检查时直接声明变量。从Java 19开始,它也支持记录:

if (o instanceof Location location) {
    System.out.println(loocation.name());
}

我们还可以使用模式匹配从模式变量location中提取值。我们可以省略模式变量location,因为调用访问器方法name()变得多余:

if (o instanceof Location (String name, GPSPoint gpsPoint)) {
    System.out.println(name);
}

如果我们有一个对象o,想要将其与记录模式进行匹配。只有当它对应记录类型的实例时,模式才会匹配。如果模式匹配成功,它会初始化变量并将其转换为相应的类型。请注意:null值不匹配任何记录模式。我们可以将变量类型替换为var,在这种情况下,编译器会为我们推断类型:

if (o instanceof Location (var name, var gpsPoint)) { 
    System.out.println(name); 
}

我们甚至可以进一步拆解GPSPoint

if (o instanceof Location (var name, GPSPoint(var latitude, var longitude))) {
    System.out.println("lat: " + latitude + ", lng: " + longitude);
}

这称为嵌套拆解。它有助于数据导航,使我们能够直接访问latitudelongitude,而无需使用Location的getter获取GPSPoint,然后在GPSPoint对象上使用getter获取latitudelongitude值。

我们也可以用于泛型记录。让我们引入一个新的泛型记录,称为Wrapper

public record Wrapper<T>(T t, String description) { }

这个记录封装任何类型的对象,并允许我们为其添加描述。我们仍然可以像以前一样使用instanceof,甚至可以拆解记录:

Wrapper<Location> wrapper = new Wrapper<>(new Location("Home", new GPSPoint(1.0, 2.0)), "Description");
if (wrapper instanceof Wrapper<Location>(var location, var description)) {
    System.out.println(description);
}

编译器也可以推断出变量location的类型。

3.2. 切换表达式

我们还可以使用切换表达式根据对象类型执行特定操作:

String result = switch (o) {
    case Location l -> l.name();
    default -> "default";
};

它会查找第一个匹配的案例。同样,我们也可以使用嵌套拆解:

Double result = switch (object) {
    case Location(var name, GPSPoint(var latitude, var longitude)) -> latitude;
    default -> 0.0;
};

我们必须记住,总是需要提供一个default案例,以处理没有匹配的情况。

如果我们想避免default案例,也可以使用密封接口和permit那些必须实现接口的对象:

public sealed interface ILocation permits Location {
    default String getName() {
        return switch (this) {
            case Location(var name, var ignored) -> name;
        };
    }
}

这有助于我们消除default案例,只创建相应的案例。

我们还可以为特定案例设置保护。例如,我们可以使用when关键字进行相等性检查,并在出现不希望的行为时引入新的Location

String result = switch (object) {
    case Location(var name, var ignored) when name.equals("Home") -> new Location("Test", new GPSPoint(1.0, 2.0)).getName();
    case Location(var name, var ignored) -> name;
    default -> "default";
};

如果这个切换表达式由以下对象调用:

Object object = new Location("Home", new GPSPoint(1.0, 2.0));

它将变量result赋值为"Test"。我们的对象是Location记录类型,其名称为"Home"。因此,它会直接跳转到开关的第一个案例。如果名称不是"Home",它会跳到第二个案例。如果对象根本不是Location类型,将返回默认值。

4. 总结

在这篇文章中,我们了解到记录模式允许我们使用模式匹配将记录的值提取到变量中。我们可以使用instanceofswitch语句,甚至带有附加条件的保护来实现这一点。记录模式在处理嵌套记录或密封记录层次结构时特别有用。

如往常一样,这些示例也可在GitHub上找到。


« 上一篇: Java Weekly, 第472期
» 下一篇: Flyway乱序迁移