学习笔记-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 Exprpermits ConstantExpr, PlusExpr, TimesExpr, NegExpr {///public void ff();
}record ConstantExpr(int i) implements Expr {@Overridepublic void ff() {System.out.printf("Constant %d\n", i);}
}
record PlusExpr(Expr a, Expr b) implements Expr {@Overridepublic void ff() {System.out.printf("Constant %d\n", a,b);}
}
record TimesExpr(Expr a, Expr b) implements Expr {@Overridepublic void ff() {System.out.printf("Constant %d\n", a,b);}
}
record NegExpr(Expr e) implements Expr {@Overridepublic 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) { }