关于record与class的区别

xW9WZ9.png


我新提的java新特性啊!

class

废话不多说直接上代码。
对于一些对象,列如一个平面里的点,或者一个范围序列,常见的用类实现:

//一个坐标

class Point {
private double x;
private double y;
//一个无参构造器
public Point() {
x = 0;
y = 0;
}

//一个自定义构造器
public Point(double x, double y) {
x = x;
y = y;
}
// 利用静态变量构造一个原点
// 静态变量属于类,无论实例化了多少个对象都共享类的
//同一个静态变量
public final static Point = new Point();

//一个计算点与点之间距离的静态方法
//静态方法可以看作是一个c语言时的函数调用时用
//类名 . + 方法名
public static double distance(Point p1, Point p2) {
return Math.hypot(p1.x - p2.x, p1.y - p2.y) ;
//要打印出结果还需要在主函数中用一个double来接收
//无法直接打印,但record有自定义的toString方法
//这意味这你可以直接打印这个实列,详情见下文record代码部分
}

}


//一个范围
class Range {
//字段
private int from;
private int to;
//无参默认构造器
public Range() {}

//一个自定义构造器
public Range(int from, int to) {

//如果from 大于 to显然对于一个范围来说不太合适需要通过这个构造器矫正
if (from > to) {
int temp = from;
from = to;
to = temp;
} else {
from = from;
to = to;
}
}

public int getFrom() {
return from;
}

public int getTo() {
return to;
}
}

通过阅读以上代码我们会发现,我们的需求远小于代码量所支持的功能,因为我们并不需要修改字段(类的属性,在文中统称为字段)的数值,只是需要以实列的方式记录一下,单拎出比如p.x这样的属性,使用类有杀鸡用牛刀的感觉所以在java16中有一个新特性record使用方法基本和class一致。

record

import java.util.*;

public class recordTest {
public static void main(String[] args) {
var p = new Point(3, 4);
System.out.println("Coordinates of p:" + p.x() + " " + p.y());
System.out.println("Distance from Origin:" + p.distanceFromOrigin());
System.out.println("Distance from Origin:" + Point.distance(Point.ORIGIN, p));
//记录可以直接将实例化的对象打印
System.out.println(p);

var pt = new PointInTime(3, 4, new Date());
System.out.println("Before: " + pt);
pt.when().setTime(0);
System.out.println("After: " + pt);

var r = new Range(4, 3);
System.out.println("r: " + r);
}
}

record Point(double x, double y) {
//无参构造器
public Point() {
this(0,0);
}
//生成原点
public static Point ORIGIN = new Point();
//计算一个坐标到原点的距离
public double distanceFromOrigin() {
return Math.hypot(x, y);
}

//计算两点间距离的静态方法,通过类名调用
//也可以通过实列对象调用啦,但根据江湖规矩还是用类名调用,不然别的程序员再维护你的代码时会痛骂你不讲武德。
public static double distance(Point p1, Point p2) {
return Math.hypot(p1.x - p2.x, p1.y - p2.y);
}



//public中有main方法这段就不会调用,主要是可以调试在终端中java ./Ponit就可以运行Point类中的main方法
//甚至在java6以前可以不带main方法打印“Hello, World!”, 简直逆天!!!
public static void main(String[] args) {
Point p1 = new Point(3, 4);
Point p2 = new Point(5, 9);

System.out.println(distance(p1, p2));

}
}

record PointInTime(double x, double y, Date when) {}

record Range(int from, int to) {
//对于一个范围来说from 大于to不合理,用构造器合理化。
public Range {
if (from > to) {
int temp = from;
from = to;
to = temp;
}
}
}

不过值得注意的是record中的字段一旦初始化以后就不能再修改
比如:

record Point(double x, double y){}
//里面的相当于有如下实例字段的类
private final double x;
private final double y;

并且不能为记录增加实例字段
record Point(double x, double y) {
private double r;//报错
}


总结一下

对于完全一组变量表示的不可变数据,要使用记录而不是类。如果数据是可变的,或者数据表示可能随着时间改变,则使用类。记录更易读、更高效,而且在并发程序中更安全。