其他
Java 14 有哪些新特性?
记录为 Java 提供了一种正确实现数据类的能力,不再需要为实现数据类而编写冗长的代码。下面就来看看 Java 14 中的记录有哪些新特性。
作者 | Nathan Esquenazi
译者 | 明明如月,责编 | 郭芮
出品 | CSDN(ID:CSDNnews)
以下为译文:
前提条件
什么是一条记录?
一个记录的例子
record Person (String firstName, String lastName) {}
Person
对象,包含 firstName
和lastName
两个组件,记录的 body 为空。--enable-preview
选项。Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
揭露其神秘面纱
Last modified Dec 22, 2019; size 1273 bytes
SHA-256 checksum 6f1b325121ca32a0b6127180eff29dcac4834f9c138c9613c526a4202fef972f
Compiled from "Person.java"
final class examples.Person extends java.lang.Record
minor version: 65535
major version: 58
flags: (0x0030) ACC_FINAL, ACC_SUPER
this_class: #8 // examples/Person
super_class: #2 // java/lang/Record
interfaces: 0, fields: 2, methods: 6, attributes: 4
Constant pool:
#1 = Methodref #2.#3 // java/lang/Record."":()V
#2 = Class #4 // java/lang/Record
#3 = NameAndType #5:#6 // "":()V
#4 = Utf8 java/lang/Record
#5 = Utf8
#6 = Utf8 ()V
#7 = Fieldref #8.#9 // examples/Person.firstName:Ljava/lang/String;
#8 = Class #10 // examples/Person
#9 = NameAndType #11:#12 // firstName:Ljava/lang/String;
#10 = Utf8 examples/Person
#11 = Utf8 firstName
#12 = Utf8 Ljava/lang/String;
#13 = Fieldref #8.#14 // examples/Person.lastName:Ljava/lang/String;
#14 = NameAndType #15:#12 // lastName:Ljava/lang/String;
#15 = Utf8 lastName
#16 = Fieldref #8.#9 // examples/Person.firstName:Ljava/lang/String;
#17 = Fieldref #8.#14 // examples/Person.lastName:Ljava/lang/String;
#18 = InvokeDynamic #0:#19 // #0:toString:(Lexamples/Person;)Ljava/lang/String;
#19 = NameAndType #20:#21 // toString:(Lexamples/Person;)Ljava/lang/String;
#20 = Utf8 toString
#21 = Utf8 (Lexamples/Person;)Ljava/lang/String;
#22 = InvokeDynamic #0:#23 // #0:hashCode:(Lexamples/Person;)I
#23 = NameAndType #24:#25 // hashCode:(Lexamples/Person;)I
#24 = Utf8 hashCode
#25 = Utf8 (Lexamples/Person;)I
#26 = InvokeDynamic #0:#27 // #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z
#27 = NameAndType #28:#29 // equals:(Lexamples/Person;Ljava/lang/Object;)Z
#28 = Utf8 equals
#29 = Utf8 (Lexamples/Person;Ljava/lang/Object;)Z
#30 = Utf8 (Ljava/lang/String;Ljava/lang/String;)V
#31 = Utf8 Code
#32 = Utf8 LineNumberTable
#33 = Utf8 MethodParameters
#34 = Utf8 ()Ljava/lang/String;
#35 = Utf8 ()I
#36 = Utf8 (Ljava/lang/Object;)Z
#37 = Utf8 SourceFile
#38 = Utf8 Person.java
#39 = Utf8 Record
#40 = Utf8 BootstrapMethods
#41 = MethodHandle 6:#42 // REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
#42 = Methodref #43.#44 // java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
#43 = Class #45 // java/lang/runtime/ObjectMethods
#44 = NameAndType #46:#47 // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
#45 = Utf8 java/lang/runtime/ObjectMethods
#46 = Utf8 bootstrap
#47 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
#48 = String #49 // firstName;lastName
#49 = Utf8 firstName;lastName
#50 = MethodHandle 1:#7 // REF_getField examples/Person.firstName:Ljava/lang/String;
#51 = MethodHandle 1:#13 // REF_getField examples/Person.lastName:Ljava/lang/String;
#52 = Utf8 InnerClasses
#53 = Class #54 // java/lang/invoke/MethodHandles$Lookup
#54 = Utf8 java/lang/invoke/MethodHandles$Lookup
#55 = Class #56 // java/lang/invoke/MethodHandles
#57 = Utf8 Lookup
{
private final java.lang.String firstName;
descriptor: Ljava/lang/String;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private final java.lang.String lastName;
descriptor: Ljava/lang/String;
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
public examples.Person(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Record."":()V
4: aload_0
5: aload_1
6: putfield #7 // Field firstName:Ljava/lang/String;
9: aload_0
10: aload_2
11: putfield #13 // Field lastName:Ljava/lang/String;
14: return
LineNumberTable:
line 3: 0
MethodParameters:
Name Flags
firstName
lastName
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(Lexamples/Person;)Ljava/lang/String;
6: areturn
LineNumberTable:
line 3: 0
public final int hashCode();
descriptor: ()I
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #22, 0 // InvokeDynamic #0:hashCode:(Lexamples/Person;)I
6: ireturn
LineNumberTable:
line 3: 0
public final boolean equals(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokedynamic #26, 0 // InvokeDynamic #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z
7: ireturn
LineNumberTable:
line 3: 0
public java.lang.String firstName();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #16 // Field firstName:Ljava/lang/String;
4: areturn
LineNumberTable:
line 3: 0
public java.lang.String lastName();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #17 // Field lastName:Ljava/lang/String;
4: areturn
LineNumberTable:
line 3: 0
}
SourceFile: "Person.java"
Record:
java.lang.String firstName;
descriptor: Ljava/lang/String;
java.lang.String lastName;
descriptor: Ljava/lang/String;
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 examples/Person
#48 firstName;lastName
#50 REF_getField examples/Person.firstName:Ljava/lang/String;
#51 REF_getField examples/Person.lastName:Ljava/lang/String;
InnerClasses:
public static final #57= #53 of #55; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
这个类被标记为 final ,意味着不能创建子类。 和所有的枚举都以 java.lang.Enum
为基类一样, 所有的记录都以java.lang.Record
为基类。两个组件: firstName
和lastName
都是用private
和final
的。有一个提供构造对象的公有构造函数: public examples.Person(java.lang.String, java.lang.String)
。通过查看它的字节码,我们可以知道,这个构造函数只是将两个参数赋值给这两个组件。该构造函数等价于:public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}有两个获取对象值的方法,分别为 firstName()
和lastName()
.自动生成 toString()
,hashCode()
和equals()
三个函数。他们都依赖invokedynamic
来实现动态调用包含隐式实现函数在内的方法。从字节码中可以看到,有一个启动函数ObjectMethods.bootstrap
来根据记录组件的名称和它的 Getter 函数,生成对应的函数。他们的表现和我们设想的一致:
System.out.println(john.firstName()); // John
System.out.println(john.lastName()); // Doe
System.out.println(john); // Person[firstName=John, lastName=Doe]
Person jane = new Person("Jane", "Dae");
Person johnCopy = new Person("John", "Doe");
System.out.println(john.hashCode()); // 71819599
System.out.println(jane.hashCode()); // 71407578
System.out.println(johnCopy.hashCode()); // 71819599
System.out.println(john.equals(jane)); // false
System.out.println(john.equals(johnCopy)); // true
在记录的声明中添加成员
static int x;
}
static int x;
public static void dox(){
x++;
}
public String getFullName(){
return firstName + " " + lastName ;
}
}
public Person {
if(firstName==null||lastName==null){
throw new IllegalArgumentException("firstName and lastName must not be null");
// 你可以忽略属性赋值,编译器会自动为你添加赋值代码
}
public Person(String fullName){
this(fullName.split("")[0], fullName.split("")[1]);
}
}
结论
热 文 推 荐
☞漫话:如何给女朋友解释为什么一到年底,部分网站就会出现日期混乱的现象?
Modified on