学习笔记-Record类

 359:Records (Preview)
384:Records (Second Preview)
395:

Records

405:Record Patterns (Preview)
432:Record Patterns (Second Preview)
440:Record Patterns

Record类由 JEP 359 于 2019 年年中提出,并于 2020 年初在 JDK 14 中作为预览特性交付。

前面三个JEP是Record类的产生后面三个PEP是Record类的功能扩展。

Record类的产生

背景

人们常常抱怨"Java 过于冗长"或"仪式感太强"。其中最糟糕的例子是一些仅用于承载少量值的不可变数据类。正确编写一个数据承载类涉及大量低价值、重复且易出错的代码:构造函数、访问器、 equals、 hashCode、 toString等方法。

Record类是Java语言中的一种新类。记录的目的是声明一小组变量应被视为一种新的实体。记录声明其状态——变量组——并承诺匹配该状态的 API。这意味着记录放弃了一个类通常享受的自由——即类 API 与其内部表示解耦的能力——但作为回报,记录变得更加简洁。

Record类代码示例

record Point(int x, int y) { }

等于

import java.util.Objects;

final class Point {
    private final int x;
    private final int y;

    Point(int x, int y) { 
        this.x = x;
        this.y = y;
    }

    int x() { return x; }
    int y() { return y; }

    public boolean equals(Object o) { 
        if (!(o instanceof Point)) return false;
        Point other = (Point) o;
        return other.x == x && other.y == y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }

    public String toString() { 
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

Record类的特性

与枚举一样,记录也是类的受限形式。它非常适合作为“数据载体”,即包含不想更改的数据的类,以及只包含最基本的方法(如构造函数和访问器)的类。

记录类的超类总是 java.lang.Record 类似于枚举类的超类总是java.lang.Enum但记录类不能显式地扩展任何类,甚至不能扩展其隐式超类 Record 

Record类成员属性都要写在后面括号里

Record类成员属性默认被final修饰,并且不能通过反射修改

Record类成员属性默认有一个public的属性访问方法,没有set方法。

有且仅有一个公共构造函数,构造函数包含了该Record类所有成员属性

有默认的equals、 hashCode、 toString方法

记录类的成员属性可以是密封类,记录类可以实现密封口

Record类和密封类型

public sealed interface Expr
        permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {
    ///
    public  void ff();
}

record ConstantExpr(int i)       implements Expr {
    @Override
    public void ff() {
        System.out.printf("Constant %d\n", i);
    }
}
record PlusExpr(Expr a, Expr b)  implements Expr {
    @Override
    public void ff() {
        System.out.printf("Constant %d\n", a,b);
    }
}
record TimesExpr(Expr a, Expr b) implements Expr {
    @Override
    public void ff() {
        System.out.printf("Constant %d\n", a,b);

    }
}
record NegExpr(Expr e)           implements Expr {
    @Override
    public void ff() {
        System.out.printf("Constant %d\n", e);

    }
}

记录类的成员属性可以是密封类,记录类可以实现密封接口。

记录与密封类型(JEP 360)配合得很好。记录和密封类型的组合有时被称为代数数据类型。记录使我们能够表达产品类型,而密封类型使我们能够表达和类型。

Record类的注解

记录组件上的字段有注解,那么该注解出现在相应的字段上和访问器方法上

record Point(@MyAnno("value")int x, int y) {
}

等于

final class Point {
    @MyAnno("value")
    private final int x;

    @MyAnno("value")
    int x() { return x; }

    //其他不变...
}

如果注解只想出现在对应的字段或者访问方法

可以对注解进行@Target注解修饰

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.RECORD_COMPONENT})
public @interface MyAnno {
    String value();
}

Record类的反射

java.lang.Class添加以下公共方法:

RecordComponent[] getRecordComponents()方法 getRecordComponents()返回一个 java.lang.reflect.RecordComponent对象的数组。该数组中的元素对应于记录的组件,顺序与它们在记录声明中出现的顺序相同。可以从数组中的每个元素中提取更多信息,包括其名称、注解和访问器方法。
boolean isRecord()方法isRecord如果给定的类被声明为记录,则返回 true。(与isEnum相比。)

        Class c =Class.forName("Point");
        if(c.isRecord()){
            RecordComponent[] recordComponents = c.getRecordComponents();
            Arrays.stream(recordComponents).forEach(System.out::println);
        }
        Method[] methods = c.getMethods();
        Arrays.stream(methods).forEach(System.out::println);

Record类的功能扩展

在 JDK 16 中,JEP 394 扩展了 instanceof运算符以接受类型模式并执行模式匹配

在 JDK 17、JDK 18 和 JDK 19 中,我们通过 JEP 406、JEP 420 和 JEP 427,将类型模式的使用扩展到了switch案例标签

增强型for语句头部的支持

instanceof

例1
    static void printSum(Object o) {
        if (o instanceof Point p) {
            int x = p.x();
            int y = p.y();
            System.out.println(x+y);
        }
    }

可以简写成

    static void printSum(Object o) {
        if (o instanceof Point(int x, int y)) {
            System.out.println(x+y);
        }
    }
例2
    enum Color { RED, GREEN, BLUE }
    record ColoredPoint(Point p, Color c) {}
    record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
    static void printUpperLeftColoredPoint(Rectangle r) {
        if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) {
            System.out.println(ul.c());
        }
    }

可以简写成

    static void printColorOfUpperLeftPoint(Rectangle r) {
        if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
                                   ColoredPoint lr)) {
            System.out.println(c);
        }
    }

switch

配合Switch必须让case详尽,或者加上default分支,都则报错

增强型for(第二次预览提出,正式上线删除)

该功能可能在未来的 JEP 中重新提出

增强1

循环中可以命名成员变量以便使用

        Point point = new Point(1, 4);
        List<Point> points = new ArrayList<>();
        points.add(point);
        for (Point(var x, var y) : points) { }
增强2

如果 R 是一个记录模式,那么形式为

for (R : e) S

等效于

for (var tmp : e) {
    switch(tmp) {
        case null -> throw new MatchException(new NullPointerException());
        case R -> S;
    }
}

Java21发布版中只能用普通模式

     Point point = new Point(1, 4);
        List<Point> points = new ArrayList<>();
        points.add(point);
        for (Point p : points) { }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值