객체간 불변 데이터를 전달시 자바 14이전에 여러가지 문제가 많은 boilerplate필드 메소드가 있는 클래스를 생성해야만 했다 하지만 자바 14릴리즈 버전 이후 레코드 클래스가 출시 되면서 이러한 문제를 해결할 수 있게 되었다.
정리 : 14이전에는 불변 객체를 생성하기 위해 불필요한 코드를 작성해야 했지만 레코드 클래스가 출시 되면서 간편하게 생성하여 불변 데이터를 전달할 수 있게 되었다.
데이터베이스 결과, 서비스 정보등 이러한 데이터를 단순히 보관하기 위해 클래스를 생성해야 하는데 대부분의 경우 불변 데이터이며 이렇게 하는 경우 데이터를 동기화 할 필요가 없이 데이터의 정확성을 보장할 수 있다.
정리 : 객체의 불변성을 보장하기에 정확성이 높아진다.
1. 각 데이터는 parivate final 필드이다.
2. 각각의 필드는 getter를 제공한다.
3. 각 필드의 맞는 해당하는 인자에 public 생성자를 제공한다.
4. 모든 필드가 일치하면 true를 반환하는 equals 메서드를 제공한다.
5. 모든 필드가 일치하면 같은 값의 해시코드를 반환하는 hashCode()를 제공한다.
6. 메서드는 클래스의 이름과 각 필드의 이름값을 포함된 문자열을 반환하는 기능인 toString()을 제공한다.
예를들어 Person 클래스와 name 과 address필드를 만들어보자
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 + "]";
}
// standard getters
}
이 코드를 작성하는 동안 몆가지의 문제가 발생한다.
1. 중복된 코드들이 많다.
2. 많은 코드로 인하여 클래스의 목적이 애매해졌다.
첫번째의 경우에는 비 효율적인 프로세스를 반복해야 하는데, 레코드 클래스는 단순하게 새로운 각각의 필드를 만들 수 있으며, equls, hashCode, toString등 메서드와 각 필드에 대한 생성자를 제공해준다.
IDE가 자동적으로 여러개의 클래스를 생성할 수 있지만 새로운 필드를 추가하면 자동적으로 클래스에 업데이트 하지는 않는다. 예를들어 새로운 필드를 추가하면 이 필드를 통합하기 위해 equals메서드를 업데이트 해야한다.
두번째의 경우 추가 코드로 인하여 본래 목적인 name과 address를 나타내는 데이터 클래스의 목적이 모호해졌다.
클래스가 데이터 클래스임을 명시하는것이 좋은 방법이다.
public record Person (String name, String address) {}
레코드 클래스를 사용하면 각 필드의 인자에 대한 public생성자를 제공해준다.
Person 레코드 클래스는 다음 예제와 같은 생성자를 제공해준다.
public Person(String name, String address) {
this.name = name;
this.address = address;
}
생성자는 클래스와 동일한 방식으로 레코드 클래스를 인스던스화 할 수 있다.
Person person = new Person("John Doe", "100 Linda Ln.");
또한 이름이 일치한 public타입의 getter메소드를 제공받을 수 있다.
Person레코드에서 name()과 address()는 getter메소드를 의미한다.
@Test
public void givenValidNameAndAddress_whenGetNameAndAddress_thenExpectedValuesReturned() {
String name = "John Doe";
String address = "100 Linda Ln.";
Person person = new Person(name, address);
assertEquals(name, person.name());
assertEquals(address, person.address());
}
equals와 비슷한 메서드인 hashCode메서드를 제공하며, Person객체의 필드값이 모두 일치하면 같은 hashCode값을 반환한다.
@Test
public void givenSameNameAndAddress_whenHashCode_thenPersonsEqual() {
String name = "John Doe";
String address = "100 Linda Ln.";
Person person1 = new Person(name, address);
Person person2 = new Person(name, address);
assertEquals(person1.hashCode(), person2.hashCode());
}
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, "Unknown");
}
}
클래스 생성자와 마찬가지로 레코드 클래스의 필드도 this키워드를 참조할 수 있으며, 인자의 이름은 필드의 이름과 일치해야 한다.
기본적으로 제공되는 생성자와 동일한 인자를 사용해야 한다. 하지만 이렇게 하려면 필드를 수동적으로 초기화 해줘야 한다.
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.address = address;
}
}
레코드 클래스도 일반 JAVA클래스와 마찬가지로 static변수, static메서드를 포함할 수 있다.
일반 클래스와 동일한 구문을 사용하여 static 변수를 선언할 수 있다.
public record Person(String name, String address) {
public static String UNKNOWN_ADDRESS = "Unknown";
}
마찬가지로 일반 클래스와 동일한 구문을 사용하여 static메서드를 선언할 수 있다.
public record Person(String name, String address) {
public static Person unnamed(String address) {
return new Person("Unnamed", address);
}
}
static변수, 메서드의 선은을 마치고 레코드 클래스의 이름을 사용하여 참조할 수 있다.
Person.UNKNOWN_ADDRESS
Person.unnamed("100 Linda Ln.");
레코드 클래스를 사용하면 기본적으로 컴파일러에서 제공되는 메서드와 함께 반복적인 코드를 줄여 불변 클래스의 안정성을 향상시킬 수 있다.
해당 코드는 깃허브에서 참조할 수 있다. https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-14