jdk 14 에서 새로운 타입 으로 record 가 preview 형태로 도입되었습니다. Enum 타입과 마찬가지로 record 타입은 몇가지 제약사항을 가지고 있으며 불변의 데이터 셋을 정의하는데 상당한 간소함을 제공합니다.
JEP 359 에 기술된 내용을 보면 record 의 도입배경과 목적 외에 여러 관련 사항들을 확인해 볼 수 있습니다.
이번 포스팅에서는 record의 제약사항 및 용법에 대해서 알아보겠습니다.
record 의 도입배경과 목적
불변의 데이터 셋 (data carrier) 역할을 하는 클래스의 경우에도 생성자, 접근자, equals(), hashCode(), toString() 등 반복적인 코드를 많이 작성해야 하는 불편함과 함께 record 는 불변하는 데이터 집합을 보다 쉽고, 명확하고, 간결하게 하기 위한 목적이 있습니다.
Lombok 과 같이 데이터를 간소화 할 수 있는 annotation 을 정의하면 코드를 간소화 할 수는 있지만 해당 클래스가 불변의 데이터 셋 역할을 한다는 설계 의도를 명확하게 파악할 수는 없습니다. record 의 사용은 불변의 데이터 셋을 나타내는 명확한 설계 의도를 나타낼 수 있습니다.
record 제약사항
- 다른 클래스를 확장할 수 없습니다.
- private final 필드 이외의 인스턴스 필드를 선언할 수 없습니다.
- private final 필드가 아닌 다른 모든 필드는 static 이어야 합니다.
- record 는 abstract 가 될 수 없으며 암묵적으로 final 입니다. (다른 클래스를 확장할 수 없음과 연결됩니다.)
- record 의 구성요소는 암묵적으로 final 입니다.
record 일반사항
- class 내에 선언할 수 있으며 중첩된 record 는 암묵적으로 static 입니다.
- record 는 inteface 를 구현할 수 있습니다.
- 일반 class 와 마찬가지로 new 키워드로 record 를 인스턴스화 합니다.
- record 본문에서 static method, static fields, static initializers, constructors, instance method 및 중첩된 유형을 선언할 수 있습니다.
- record 및 record 개별 구성 요소에 annotation 을 붙일 수 있습니다.
record 기본 용법
package com.example.test.record;
import lombok.Builder;
import java.util.UUID;
final public class Student {
private final UUID uuid;
private final String name;
private final String grade;
@Builder
public Student(UUID uuid, String name, String grade) {
this.uuid = uuid;
this.name = name;
this.grade = grade;
}
public UUID uuid() {
return uuid;
}
public String name() {
return name;
}
public String grade() {
return grade;
}
}
Student class 는 uuid(), name(), grade() 3개의 getter 메소드와 모든 요소를 파라미터로 가지는 생성자로 구성되어 있습니다.
위 클래스를 record 를 사용하면 아래와 같이 선언할 수 있습니다.
package com.example.test.record;
import lombok.Builder;
import java.util.UUID;
@Builder
public record Student(UUID uuid, String name, String grade) {}
record 는 아래의 내용을 자동으로 처리한다.
- 각 구성요소를 private final 필드로 처리합니다.
- 각 구성요소의 이름과 동일한 public getter 메소드를 제공합니다. uuid(), name(), grade()
- 모든 구성요소를 포함하는 public 생성자를 제공합니다.
- 모든 구성 요소에 대한 equals(), hashCode(), toString() 를 생성합니다.
equals(), hashCode(), toString() 에 대한 내용은 아래 테스트 코드를 통해서 검증이 가능합니다.
@Test
void record_test() {
UUID uuid = UUID.randomUUID();
String name = "student1";
String grade = "A";
Student record = Student.builder()
.uuid(uuid)
.name(name)
.grade(grade)
.build();
Student record2 = Student.builder()
.uuid(uuid)
.name(name)
.grade(grade)
.build();
Assertions.assertThat(record.hashCode()).isEqualTo(record2.hashCode());
Assertions.assertThat(record.toString()).isEqualTo(record2.toString());
Assertions.assertThat(record.equals(record2)).isTrue();
}
Compact Constructors
record 에서 자동 생성한 생성자가 각 필드를 초기화 하는 것 이상의 작업을 수행하도록 하려면 record 에 대한 사용자 지정 생성자를 정의 할 수 있습니다. 이 생성자 정의를 Compact Constructor 라고 하며 argument 정의가 없습니다.
public record Student(UUID uuid, String name, String grade) {
public Student {
Objects.requireNonNull(uuid);
Objects.requireNonNull(name);
Objects.requireNonNull(grade);
}
}
또한 record 의 모든 필드에 대한 public 생성자 이외에 생성자 구현을 사용자 정의 할 수 있습니다.
public record Student(UUID uuid, String name, String grade) {
public Student(String uuid) {
this(uuid, "unknown", "unknown");
}
}
record 가 생성한 public 생성자와 동일한 argument 를 사용하여 생성자를 생성할 수 있지만, 이를 위해서는 각 필드를 수동으로 초기화 해야 합니다.
public record Student(UUID uuid, String name, String grade) {
public Student(UUID uuid, String name, String grade) {
this.uuid = uuid;
this.name = name;
this.grade = grade;
}
}
Compact 생성자와 모든 argument 목록을 정의한 생성자가 함께 정의된 경우에는 compile error 가 발생합니다.
//compile error!!!
public record Student(UUID uuid, String name, String grade) {
public Student {
Objects.requireNonNull(uuid);
Objects.requireNonNull(name);
Objects.requireNonNull(grade);
}
public Student(UUID uuid, String name, String grade) {
this.uuid = uuid;
this.name = name;
this.grade = grade;
}
}
Static 변수와 메소드
private final 필드 이외의 필드는 static 필드여야 하고, record 는 static 메소드를 가질 수 있습니다.
package com.example.test.record;
import lombok.Builder;
import java.util.UUID;
@Builder
public record Student(UUID uuid, String name, String grade) {
public static String UNKNOWN_NAME = "unknown";
public static Student unknownStudent(UUID uuid, String grade) {
return Student.builder()
.uuid(uuid)
.name(UNKNOWN_NAME)
.grade(grade)
.build;
}
}
@Test
void record_static_method_test() {
UUID uuid = UUID.randomUUID();
String grade = "A";
StudentRecord unknownStudent = StudentRecord.unknownStudent(uuid, grade);
Assertions.assertThat(unknownStudent.name()).isEqualTo(StudentRecord.UNKNOWN_NAME);
}
지금까지 java record 에 대해서 알아 보았습니다. 도움이 되었기를 바랍니다.
'자바' 카테고리의 다른 글
jdk pattern matching for instanceof (0) | 2023.08.26 |
---|---|
java switch expression - from jdk 14 (0) | 2023.08.20 |
java Array vs ArrayList (0) | 2023.08.19 |
CompletableFuture 를 알아보자 (0) | 2023.08.06 |
byte 배열에서 charset 정보 detecting 하기 (0) | 2023.08.04 |
댓글