자바 레코드(Record)를 알아보자
자바 14이전에는 불변필드 , 메서드를 클래스내에서 생성할 때, 혼란스러운 의도 그리고 사소한 실수에 취약한 클래스를 생성했다.
레코드는 자바 14에서 등장하였고 자바 16에서 정식기능으로 배포되었다.
사실 상 dto나 데이터 도메인을 사용할 때, 클래스 자체가 처음 값을 할당한다면 불변객체이어야 하는 경우가 많다.
예를 들어서, 회원가입 시에, 프론트엔드에서 데이터를 요청을 통해서 받았을 때, 값이 바뀌는 경우는 없을 것이다. 이러한 경우를 위해서 Record를 만들었다.
public class Person {
private final String name;
private final String address;
public Person(String name, String address){
this.name = name;
this.address = address;
}
@Override
public int hashCode() {
return Objects.hash(name, address);
}
@Override
public boolean equals(Object obj){
if(this == obj) {
return true;
} else if ( !(obj instanceof Person)) {
return false;
} else {
Person other = (Person) obj;
return Objects.equals(name, other.name)
&& Objects.equals(address, other.address);
}
}
@Override
public String toString() {
return "Person [name = " + name + ", address=" + address + "]";
}
위에 코드에는 문제점이 많다.
1. boilerplate code가 많다.
각 데이터 클래스에 동일한 지루한 프로세스를 반복해야한다.
(equals, hashCode 및 toString 메서드를 생성하고, 각 필드를 받아들이는 생성자를 생성해야 한다.)
IDE는 이러한 많은 클래스를 자동으로 생성할 수 있지만, 새 필드를 추가할 때 클래스를 자동으로 업데이트하지는 못합니다.
ex) 새 필드를 추가하면 이 필드를 통합하기 위해 equals 메서드를 업데이트해야 합니다.
2. 이름과 주소가 있는 사람을 표현한다는 클래스의 목적이 모호해집니다.
추가 코드로 인해 클래스가 단순히 이름과 주소라는 두 개의 String 필드가 있는 데이터 클래스라는 사실이 모호해집니다.
더 나은 방법은 클래스가 데이터 클래스임을 명시적으로 선언하는 것이다.
JDK 14부터는 반복적인 데이터 클래스를 레코드로 대체할 수 있다.
레코드는 필드 유형과 이름만 필요한 불변 데이터 클래스입니다.
equals, hashcode, tostring 메서드와 private, final field, public contructor는 Java 컴파일러에 의해 생성됩니다.
클래스가 데이터 클래스임을 명시적으로 선언
jdk 14부터는 반복적인 데이터 클래스를 레코드로 대체할 수 있음.
레코드는 필드 유형과 이름만 필요한 불변 데이터 클래스이다.
equals, hashcode, tostring 메서드와 private, final field, public constructor는 Java컴파일러에 의해 생성된다.
Person record를 생성하기 위해서는 record keyword만 사용하면 된다.
public record Person(String name, String address) {}
@Constructor
record를 생성하면 각 필드에 argument가 있는 public constructor가 생성됩니다.
public Person(String name, String address) {
this.name = name;
this.address = address;
}
이 생성자는 클래스와 같은 방식으로 레코드로부터 객체를 인스턴스화하는데 사용
@Getter
필드 이름과 이름이 일치하는 Getter메서드도 사용할 수 있습니다.
Person 레코드에서는 name()및 address() getter를 의미한다.
@Test
public void givenValidNameAndAddress_whenGetNameAndAddress_thenExpectedValuesReturned(){
String name = "Gom";
String address = "55 GomeHouse";
Person person = new Person(name, address);
assertEquals(name, person.name());
assertEquals(address, person.address());
}
equals()
equals 메서드가 생성됩니다.
이 메서드는 제공된 객체의 유형이 동일하고 모든 필드 값이 일치하는 경우 true를 반환합니다.
@Test
public void givenSameNameAndAddress_whenEquals_thenPersonEqual() {
String name = "Gome";
String address = "50 GomeHouse";
Person person1 = new Person(name, address);
Person person2 = new Person(name, address);
assertTrue(person1.equals(person2));
}
두 Person 인스턴스 간에 필드 중 하나라도 다른 경우 같음 메서드는 false로 반환
hashCode
해시코드 메서드도 생성됩니다.
두 개체의 필드 값이 모두 일치하는 경우 두 Person개체에 대해 동일한 값을 반환하는 hashCode 메서드를 생성한다.
@Test
public void givenSameNameAndAddress_whenEquals_thenPersonEqual() {
String name = "Gome";
String address = "50 GomeHouse";
Person person1 = new Person(name, address);
Person person2 = new Person(name, address);
aseertEquals(person1.hashCode(), person2.hashCode());
}
필드 값 중 하나라도 다르면 해시코드 값이 달라집니다.
toString
마지막으로, 레코드의 이름과 각 필드의 이름 및 대괄호 안의 해당 값이 포함된 문자열을 반환하는 toString 메서드도 수신합니다.
따라서 이름이 "Gom"이고 주소가 "50 GomeHouse"인 Person을 인스턴스화하면 다음과 같은 toString 결과가 반환됩니다.
Person[name=Gom, address=50 GomeHouse.]
컴팩트 생성자 (Compact Constructor)
레코드의 생성자가 비공개 필드를 초기화하는 것 이상의 작업을 수행하도록 하려면 레코드에 대한 사용자 지정 생성자를 정의할 수 있습니다.
그러나 클래스 생성자와 달리 레코드생성자는 매개변수 목록이 없고, 이를 컴팩트 생성자라고 한다.
일반적으로 생성하는 표준 생성자와는 달리 컴팩트 생성자 내부에서는 인스턴스 필드에 접근할 수 없습니다.
이러한 이유로 컴팩트 생성자는 컴포넌트로 들어온 값을 불변으로 만들거나, 불변식이 만족하는지(유효성 체크, 예를 들어 null check)등의 작업을 하기에 적합함.
예시)
public record Person(String name, String address) {
public Person {
Objects.requireNonNull(name);
Objects.requireNonNull(address);
}
}
public record Person(String name, String address) {
public Person(String name) {
this(name, "Unkown");
}
}
클래스 생성자와 마찬가지로 필드를 참조할 수 있다.
생성된 public 생성자와 동일한 argument를 사용하여 생성자를 생성하는 것도 유효하지만 각 필드를 수동으로 초기화해야 한다.
public record Person(String name, String address) {
public Person(String name, String address) {
this.name = name;
this.address =address;
}
}
컴팩트한 생성자와 생성된 생성자와 일치하는 인수 목록을 가진 생성자를 선언하면 컴파일 오류가 발생합니다.
public record Person(String name, String address) {
public Person {
Objects.requireNonNull(name);
Objects.requireNonNull(address);
}
public Person(String name , String address) {
this.name = name;
this.adress =address;
}
}
위에 코드는 record와 매개변수가 똑같기 때문에 에러가 발생함.
레코드 제약 사항
레코드는 다른 클래스를 상속받을 수 없으며, private final fields이외의 인스턴스 필드를 선언할 수 없습니다.
선언되는 다른 모든 필드는 static 이어야 합니다.
이러한 제약 사항은 상태 설명만으로 표현을 정의할 수 있도록 보장해 준다.
레코드는 암시적으로 final 이며 abstract 일 수 없다.
이러한 제약 사항은 레코드의 API가 상태 설명에 의해서만 정의되며 나중에 다른 클래스나 레코드에 의해 변경될 or 나아 질 수 없음을 강조한다.
위의 제한 사항 외에도 레코드는 최상위 레벨로 선언되거나 중첩될 수 있고, 제네릭이 될 수 있으며, 인터페이스를 구현할 수 있고, 새 키워드를 통해 인스턴스화 되는 등 일반 클래스처럼 작동한다.
출처 : https://colevelup.tistory.com/28
[Java] Java14 레코드(Record)를 알아보자
Java 8 , 11 버전만 사용하다 보니 이후 버전에 추가된 것들에 대해서 학습을 하려고 합니다. Java 14에서 추가된 레코드(Record)에 대해서 레코드의 목적, 자동생성 항목 등 레코드의 기본 사항에 대해
colevelup.tistory.com
https://velog.io/@pp8817/record
✏️ [Java] record에 대하여
Java의 record에 대해서 알아봅시다.
velog.io