实战 Java 16 值类型 Record - 2. Record 的基本用法
在上一篇文章实战 Java 16 值类型 Record - 1. Record 的默认方法使用以及基于预编译生成相关字节码的底层实现中,我们详细分析了 Record 自带的属性以及方法和底层字节码与实现。这一篇我们来详细说明 Record 类的用法。
声明一个 Record
Record 可以单独作为一个文件的顶级类,即:
User.java 文件:
public record User(long id, String name, int age) {}
也可以作为一个成员类,即:
public class RecordTest {
public record User(long id, String name, int age) {}
}
也可以作为一个本地类,即:
public class RecordTest {
public void test() {
record Mail (long id, String content){}
Mail mail = new Mail(10, "content");
}
}
不能用 abstract 修饰 Record 类,会有编译错误。
可以用 final 修饰 Record 类,但是这其实是没有必要的,因为 Record 类本身就是 final 的。
成员 Record 类,还有本地 Record 类,本身就是 static 的,也可以用 static 修饰,但是没有必要。
和普通类一样,Record 类可以被 public, protected, private 修饰,也可以不带这些修饰,这样就是 package-private 的。
和一般类不同的是,Record 类的直接父类不是 java.lang.Object 而是 java.lang.Record。但是,Record 类不能使用 extends,因为 Record 类不能继承任何类。
Record 类的属性
一般,在 Record 类声明头部指定这个 Record 类有哪些属性:
public record User(long id, String name, int age) {}
同时,可以在头部的属性列表中运用注解:
@Target({ ElementType.RECORD_COMPONENT})
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}
@Target({ ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
public record User(@A @B long id, String name, int age) {}
但是,需要注意一点,这里通过反射获取 id 的注解的时候,需要通过对应的方式进行获取,否则获取不到,即 ElementType.FIELD 通过 Field 获取,ElementType.RECORD_COMPONENT 通过 RecordComponent 获取:
Field[] fields = User.class.getDeclaredFields();
Annotation[] annotations = fields[0].getAnnotations(); // 获取到注解 @B
RecordComponent[] recordComponents = User.class.getRecordComponents();
annotations = recordComponents[0].getAnnotations(); // 获取到注解 @A
Record 类体
Record 类属性必须在头部声明,在 Record 类体只能声明静态属性:
public record User(long id, String name, int age) {
static long anotherId;
}
Record 类体可以声明成员方法和静态方法,和一般类一样。但是不能声明 abstract 或者 native 方法:
public record User(long id, String name, int age) {
public void test(){}
public static void test2(){}
}
Record 类体也不能包含实例初始化块,例如:
public record User(@A @B long id, String name, int age) {
{
System.out.println(); //编译异常
}
}
Record 成员
Record 的所有成员属性,都是 public final 非 static 的,对于每一个属性,都有一个对应的无参数返回类型为属性类型方法名称为属性名称的方法,即这个属性的 accessor。前面说这个方法是 getter 方法其实不太准确,因为方法名称中并没有 get 或者 is 而是只是纯属性名称作为方法名。
这个方法如果我们自己指定了,就不会自动生成:
public record User(long id) {
@Override
public long id() {
return id;
}
}
如果没有自己指定,则会自动生成这样一个方法:
- 方法名就是属性名称
- 返回类型就是对应的属性类型
- 是一个 public 方法,并且没有声明抛出任何异常
- 方法体就是返回对应属性
- 如果属性上面有任何注解,那么这个注解如果能加到方法上那么也会自动加到这个方法上。例如:
public record User(@A @B long id, String name, int age) {}
@Target({
ElementType.RECORD_COMPONENT,
ElementType.METHOD,
})
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}
@Target({ ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
下面获取 id() 这个方法的注解则会获取到注解 @A
Method id = User.class.getDeclaredMethod("id");
Annotation[] idAnnotations = id.getAnnotations(); //@A
由于会自动生成这些方法,所以 Record 成员的名称不能和 Object 的某些不符合上述条件(即上面提到的 6 条)的方法的名称一样,例如:
public record User(
int wait, //编译错误
int hashcode, //这个不会有错误,因为 hashcode() 方法符合自动生成的 accessor 的限制条件。
int toString, //编译错误
int finalize //编译错误) {
}
Record 类如果没有指定,则默认会生成实现 java.lang.Record 的抽象方法,即hashcode(), equals(), toStrng() 这三个方法。这三个方法的实现方式,在第一节已经详细分析过,这里简单回顾下要点:
hashcode()在编译的时候自动生成字节码实现,核心逻辑基于ObjectMethods的makeHashCode方法,里面的逻辑是对于每一个属性的哈希值移位组合起来。注意这里的所有调用(包括对于ObjectMethods的方法调用以及获取每个属性)都是利用MethodHandle实现的近似于直接调用的方式调用的。equals()在编译的时候自动生成字节码实现,核心逻辑基于ObjectMethods的makeEquals方法,里面的逻辑是对于两个 Record 对象每一个属性判断是否相等(对于引用类型用Objects.equals(),原始类型使用 ==),注意这里的所有调用(包括对于ObjectMethods的方法调用以及获取每个属性)都是利用MethodHandle实现的近似于直接调用的方式调用的。toString()在编译的时候自动生成字节码实现,核心逻辑基于ObjectMethods的makeToString方法,里面的逻辑是对于每一个属性的值组合起来构成字符串。注意这里的所有调用(包括对于ObjectMethods的方法调用以及获取每个属性)都是利用MethodHandle实现的近似于直接调用的方式调用的。
Record 构造器
如果没有指定构造器,Record 类会自动生成一个以所有属性为参数并且给每个属性赋值的构造器。我们可以通过两种方式自己声明构造器:
第一种是明确声明以所有属性为参数并且给每个属性赋值的构造器,这个构造器需要满足:
- 构造器参数需要包括所有属性(同名同类型),并按照 Record 类头的声明顺序。
- 不能声明抛出任何异常(不能使用 throws)
- 不能调用其他构造器(即不能使用 this(xxx))
- 构造器需要对于每个属性进行赋值。
对于其他构造器,需要明确调用这个包含所有属性的构造器:
public record User(long id, String name, int age) {
public User(int age, long id) {
this(id, "name", age);
}
public User(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
第二种是简略方式声明,例如:
public record User(long id, String name, int age) {
public User {
System.out.println("initialized");
id = 1000 + id;
name = "prefix_" + name;
age = 1 + age;
//在这之后,对每个属性赋值
}
}
这种方式相当于省略了参数以及对于每个属性赋值,相当于对这种构造器的开头插入代码。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
实战 Java 16 值类型 Record - 2. Record 的基本用法的更多相关文章
- JAVA中值类型和引用类型的不同(面试常考)
转载:https://www.cnblogs.com/1ming/p/5227944.html 1. JAVA中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个 ...
- 【转】Java 有值类型吗?
Java 有值类型吗? 有人看了我之前的文章『Swift 语言的设计错误』,问我:“你说 Java 只有引用类型(reference type),但是根据 Java 的官方文档,Java 也有值类型( ...
- java中值类型和引用类型的区别
[定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变. 值类型表示复制一个当前变量传给方法,当你 ...
- java中值类型与引用类型的关系
值类型:就是java的基本类型.byte.short.int.long.float.char.double.boolean 引用类型:类(class).接口(Interface).数组(Array) ...
- Java的值类型和引用类型
一.问题描述 前几天因为一个需求出现了Bug.说高级点也挺高级,说白点也很简单.其实也就是一个很简单的Java基础入门时候的值类型和引用类型的区别.只是开发的时候由于自己的问题,导致小问题的出现.还好 ...
- Controller 中Action 返回值类型 及其 页面跳转的用法
•Controller 中Action 返回值类型 View – 返回 ViewResult,相当于返回一个View 页面. -------------------------------- ...
- Java 16 新特性:record类
以前我们定义类都是用class关键词,但从Java 16开始,我们将多一个关键词record,它也可以用来定义类.record关键词的引入,主要是为了提供一种更为简洁.紧凑的final类的定义方式. ...
- 学习记录 java 值类型和引用类型的知识
1. Java中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值, 那么调用这个方法是传入的变量的值也将改变 ...
- java包装类和值类型的关系
java包装类总是让人疑惑 它与值类型到底是怎么样一种关系? 本文将以int和Integer为例来探讨它们的关系 java值类型有int short char boolean byte long fl ...
随机推荐
- c++ 动态解析PE导出表
测试环境是x86 main #include <iostream> #include <Windows.h> #include <TlHelp32.h> #incl ...
- Renice INC:不同颜色的酒帽所代表的意义
酒帽就是酒瓶上方的热缩胶帽/锡帽/蜡封,也就是开瓶前要割掉的那一层保护物,所有的法国酒在酒帽上,都会有一个圆形贴纸,除了有不同颜色外,上面还有一串号码,有可能很多人在喝酒时都不会对这个酒帽有更多的在意 ...
- Android 比较好看的注册登录界面
各位看官姥爷: 对于一款android手机app而言,美观的界面使得用户有好的使用体验,而一款好看的注册登录界面也会给用户好的用户体验,那么话不多说,直接上代码 首先是一款简单的界面展示 1.登陆界面 ...
- springCloud中的服务调用feign
springCloud中的服务调用(要在调用端写) 前提进行了服务注册 流程: 1.在服务模块中添加依赖 <!--服务调用--> <dependency> <groupI ...
- SpringBoot(十一): Spring Boot集成Redis
1.在 pom.xml 中配置相关的 jar 依赖: <!-- 加载 spring boot redis 包 --> <dependency> <groupId>o ...
- 注册 Amazon Web Services(AWS) 账号,助园一臂之力
感谢大家去年的大力支持,今年园子继续和 Amazon Web Services(AWS) 合作,只要您通过 博客园专属链接 注册一个账号(建议使用手机4G网络注册),亚马逊就会给园子收入,期待您的支持 ...
- 后端程序员之路 18、朴素贝叶斯模型(Naive Bayesian Model,NBM)
贝叶斯推断及其互联网应用(一):定理简介 - 阮一峰的网络日志http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.ht ...
- XXL-JOB v2.3.0 发布 | 易用性增强
转: XXL-JOB v2.3.0 发布 | 易用性增强 v2.3.0 Release Notes 1.[新增]调度过期策略:调度中心错过调度时间的补偿处理策略,包括:忽略.立即补偿触发一次等: 2. ...
- LeetCode-二叉搜索树的第k大节点
二叉搜索树的第k大节点 LeetCode-面试题54 需要充分了解二叉搜索树的性质. 利用中序遍历的思想,采用相反的思想:先遍历右节点再遍历左节点. /** * 给定一棵二叉搜索树,请找出其中第k大的 ...
- HDU_6693 Valentine's Day 【概率问题】
一.题目 Valentine's Day 二.分析 假设$ s_0 $代表不开心的概率,$ s_1 $代表开心一次的概率. 那么随便取一个物品,那么它的开心概率为$ p _i $,可以推导加入之后使女 ...
