그리미
자바 Record 에 관해서 본문
안정적인 프로그래밍을 위해선 불변 객체가 중요하다.
Record 를 활용하면 이러한 불변 객체를 만들 수 있으며,
개발자가 별도의 toString, hashcode, equals 와 같은 메서드를 만들지 않아도 되는 장점이 있다.
사전에 정의된 메서드를 INVOKEDYNAMIC 하기 때문이다.
간단히 작성된 학생 클래스로 클래스와 레코드를 비교해보겠습니다.
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Student student = (Student)o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public record RStudent(
String name,
Integer age
) {
}
hashcode와 equals 만 바이트코드를 디컴파일해서 확인해보자
// Student 클래스
public equals(Ljava/lang/Object;)Z
// parameter o
L0
LINENUMBER 16 L0
ALOAD 0
ALOAD 1
IF_ACMPNE L1
L2
LINENUMBER 17 L2
ICONST_1
IRETURN
L1
LINENUMBER 18 L1
FRAME SAME
ALOAD 1
IFNULL L3
ALOAD 0
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
ALOAD 1
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
IF_ACMPEQ L4
L3
LINENUMBER 19 L3
FRAME SAME
ICONST_0
IRETURN
L4
LINENUMBER 20 L4
FRAME SAME
ALOAD 1
CHECKCAST com/c4/week5/Test/Student
ASTORE 2
L5
LINENUMBER 21 L5
ALOAD 0
GETFIELD com/c4/week5/Test/Student.name : Ljava/lang/String;
ALOAD 2
GETFIELD com/c4/week5/Test/Student.name : Ljava/lang/String;
INVOKESTATIC java/util/Objects.equals (Ljava/lang/Object;Ljava/lang/Object;)Z
IFEQ L6
ALOAD 0
GETFIELD com/c4/week5/Test/Student.age : Ljava/lang/Integer;
ALOAD 2
GETFIELD com/c4/week5/Test/Student.age : Ljava/lang/Integer;
INVOKESTATIC java/util/Objects.equals (Ljava/lang/Object;Ljava/lang/Object;)Z
IFEQ L6
ICONST_1
GOTO L7
L6
FRAME APPEND [com/c4/week5/Test/Student]
ICONST_0
L7
FRAME SAME1 I
IRETURN
L8
LOCALVARIABLE this Lcom/c4/week5/Test/Student; L0 L8 0
LOCALVARIABLE o Ljava/lang/Object; L0 L8 1
LOCALVARIABLE student Lcom/c4/week5/Test/Student; L5 L8 2
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x1
public hashCode()I
L0
LINENUMBER 26 L0
ICONST_2
ANEWARRAY java/lang/Object
DUP
ICONST_0
ALOAD 0
GETFIELD com/c4/week5/Test/Student.name : Ljava/lang/String;
AASTORE
DUP
ICONST_1
ALOAD 0
GETFIELD com/c4/week5/Test/Student.age : Ljava/lang/Integer;
AASTORE
INVOKESTATIC java/util/Objects.hash ([Ljava/lang/Object;)I
IRETURN
L1
LOCALVARIABLE this Lcom/c4/week5/Test/Student; L0 L1 0
MAXSTACK = 4
MAXLOCALS = 1
}
// RStudent 레코드 클래스


앞서 언급했듯 레코드는 사전 정의된 메서드를 INVOKEDYNAMIC 하다보니,
클래스에 비해 가볍다
equals 와 hashcode 가 자주 호출되는 set 에 넣는 실험을 통해
이게 성능상 어떤 영항을 주는 지 확인해보자.
먼저 Student 객체를 넣어보겠다.

다음으론 RStudent (Recored 클래스) 를 넣어보겠다.

각각 50번의 실험 결과
종류 | 속도 |
Student 클래스 (일반 클래스) | 1.71 ~ 1.85초 |
RStudent 클래스 (Record 클래스) | 1.25 ~ 1.39초 |
RStudent 가 Student 보다
대략 35% 정도 더 빠른 것을 확인 할 수 있다.