Java

[Java] Record

DH_0518 2024. 4. 5. 20:15

Record는 Java 14에서 추가된 불변 데이터 클래스로, 그 자체로 '값'을 나타내는 클래스(즉, Value Object)를 좀 더 쉽게 생성 및 수정할 수 있게 해준다. Record 도입 이전의 VO를 보고 어떤 문제를 해결하기 위해 Record가 도입되었는지를 확인해보자

 

 

Record 이전의 VO

 

 

VO는 다음의 특징들을 가지고 있는 클래스를 말한다

  • DTO처럼 단순한 데이터들의 집합이 아닌, 객체 그 자체가 '값'을 나타낸다
  • 반드시 불변 객체(immutable Object)로 사용해야 하기에 한번 생성한 후 수정할 수 없고, 항상 새로운 객체를 만들어야 한다
  • 비즈니스 로직을 포함할 수 있다 (Setter와 같이 값을 수정할 수 있는 로직은 제외)

VO 예시를 보자

@Getter
public class Person {
    // 필드 선언
    private final String name;
    private final String address;

    // 생성자
    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }

    // hashCode와 equals
    @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);
        }
    }

    // toString
    @Override
    public String toString() {
        return "Person [name=" + name + ", address=" + address + "]";
    }

}

 

먼저 불변을 위해 final로 필드가 선언되어있고, 객체 비교를 위해 hashCode와 equals가 오버라이딩 되어있다. 언뜻 보면 간단하고 필요한것만 들어가있는 것 같지만 다음과 같은 문제가 있다

  1. boilerplate code가 많다
    • 새로운 VO 클래스를 만들때마다 constructor, hashCode, equals, 그리고 toString 등의 보일러 플레이트 코드를 똑같이 매번 작성해야하는 수고로움이 있다.
    • 또한 새로운 필드를 추가하거나 기존의 필드를 삭제한다면, 이에 맞춰서 코드를 업데이트 해주어야 한다
  2. '값' 그 자체를 나타낸다는 VO의 목적이 모호해진다
    • 필드가 아닌, 추가적인 코드들에 의해서 단순히 '값'만을 표현한다는 VO라는 사실이 모호해진다

 

 

 

Record를 사용한 VO

 

 

그렇다면 위와 같은 문제점을 어떻게 Record를 통해서 해결할 수 있는지를 알아보자

 

Record

  • 생성
    • Record Keyword(필드)를 파라미터로 넘기기만 하면 된다
    • equals(), hashCode(), toString() 메서드 및 private, final field, public constructor는 Java 컴파일러에 의해 자동으로 생성된다
  • Constructor
    • 모든 필드가 포함된 public AllArgs Constructor가 생성된다 
  • Getter
    • 필드이름과 동일한 getter 메서드가 생성된다
      • ex) record.필드명()
    • 'get필드명' 으로 getter를 사용하고 싶다면, record keyword 앞에 lombok의 @Getter 어노테이션을 붙여주면 된다
      • ex) public record RecordClass(@Getter String field) {}
      • 사용 -> getField();
  • equals(), hashCode()
    • equals()는, 두 객체의 유형이 동일하고 모든 필드값이 일치하는 경우 true를 반환하도록 override 되어있음
    • hashCode()는, 두 객체의 필드값이 모두 일치하는 경우 동일한 값을 반환하도록 override 되어있음
  • toString()
    • Record 이름 +  각 필드의 name과 value가 "[]" 안에 포함된 문자열을 반환
    • ex) Record이름[필드name1=필드value1, 필드name2=필드value2, ...] 
// 생성
public record RecordTest(String name, int value, @Getter String address) {}
		
// 생성자로 생성
RecordTest recordTest = new RecordTest("kdh", 28, "Busan");

// Getter
recordTest.name(); // 결과: kdh
recordTest.value(); // 결과: 28
recordTest.address(); // 결과: Busan
recordTest.getAddress(); // 결과: Busan

// toString()
System.out.println(recordTest); // 결과: RecordTest[name=kdh, value=28, address=Busan]

// equals
recordTest.equals(new RecordTest("kdh", 28, "Busan")); // 결과: true

// hashCode
recordTest.hashCode() == new RecordTest("kdh", 28, "Busan").hashCode(); // 결과: true

 

위와같이 개발자가 Object에 필요한 메서드를 일일이 오버라이딩 할 필요가 없고, 레코드의 필드 값만 명시함으로써 boilerplate code를 줄이고 클래스의 목적을 명확하게 나타낼 수 있다

 

 

Static Variables & Method

  • 정적 변수와 메서드는, 일반적인 클래스에서 선언하는 것과 동일한 방식으로 선언한다
  • 선언 이후에는 다른 필드와 동일한 방식으로 참조할 수 있다
// 정적 변수 & 메서드 선언
public record RecordTest(String name, int value) {
	public static String staticName = "staticName";

	public static void staticMethod(String name, int value) {
		System.out.println("Name field is :" + name);
		System.out.println("Value field is :" + value);
	}
}

// 정적 변수 & 메서드 사용
System.out.println("staticName = " + RecordTest.staticName); // 결과: staticName
RecordTest.staticMethod("kdh", 28); // 결과: Name field is :kdh, Value field is :28

 

 

제약사항

  • Record는 다른 클래스를 상속받을 수 없다
  • private final 이외의 인스턴스 필드를 선언할 수 없다
  • 직접 선언하는 다른 모든 필드는 static이어야 한다
  • Record는 암시적으로 final이며, abstract 일 수 없다

위의 제약사항을 통해 레코드의 API가 상태 설명에 의해서만 정의되며, 나중에 다른 클래스나 레코드에 의해 변경될 수 없음을 강조한다

 

 

 

 

 

 

 

 

 

Reference

 

[Java] Record | 👨🏻‍💻 Tech Interview

[Java] Record Java 14버전부터 도입되고 16부터 정식 스펙에 포함된 Record는 class처럼 타입으로 사용이 가능하다. 객체를 생성할 때 보통 아래와 같이 개발자가 만들어야한다. 클래스 Person 을 만든다.

gyoogle.dev

 

[Java] Java14 레코드(Record)를 알아보자

Java 8 , 11 버전만 사용하다 보니 이후 버전에 추가된 것들에 대해서 학습을 하려고 합니다. Java 14에서 추가된 레코드(Record)에 대해서 레코드의 목적, 자동생성 항목 등 레코드의 기본 사항에 대해

colevelup.tistory.com