BackEnd/Java

Java 16. Record Class

hanseom 2024. 12. 8. 09:00
반응형

Record Class

  Java 14 preview, Java 16 정식으로 출시된 기능으로 데이터 전달을 위해 등장한 클래스입니다.

 

  다음은 데이터 전달을 위한 DTO 클래스입니다.

import java.time.LocalDate;
import java.util.Objects;

public final class FruitDto {
  private final String name; // 과일 이름
  private final int price; // 과일 가격
  private final LocalDate date; // 과일 입고 일자
  
  public FruitDto(String name, int price, LocalDate date) {
    this.name = name;
    this.price = price;
    this.date = date;
  }
  
  public String name() {
    return name;
  }
  
  public int price() {
    return price;
  }
  
  public LocalDate date() {
    return date;
  }
  
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    FruitDto fruitDto = (FruitDto) o;
    return price == fruitDto.price && Objects.equals(name, fruitDto.name) && Objects.equals(date, fruitDto.date);
  }
  
  @Override
  public int hashCode() {
    return Objects.hash(name, price, date);
  }
  
  @Override
  public String toString() {
    return "FruitDto[" +
        "name='" + name + '\'' +
        ", price=" + price +
        ", date=" + date +
        ']';
  }
}
  • class에 final이 선언되어 있어서 다른 클래스가 상속 받지 못합니다.
  • private final 필드만 선언되어 있습니다.
  • 모든 private final 필드에 대해 생성자가 존재합니다.
  • 클래스가 갖고 있는 모든 필드에 접근할 수 있는 메소드가 있고, 메소드의 이름은 필드의 이름과 동일합니다.
  • equals() / hashCode() / toString()이 존재합니다.

 

  개발을 하다보면 이러한 데이터 전달 객체 (DTO)를 꽤 많이 만들게 됩니다. 다음은 위의 FruitDto 클래스를 Record 클래스로 구현한 코드입니다.

public record FruitDtoV2(
    String name, // 컴포넌트(Component)
    int price,
    LocalDate date
) {
}
  • 일반적인 클래스와 다르게 ()로 시작합니다.
  • () 안에는 Record 클래스가 갖고 있을 필드들을 선언합니다.
  • FruitDtoV2 Record 클래스는 FruitDto 클래스와 완전히 동일합니다.
  • 다른 클래스가 상속 받을 수 없습니다.
  • 컴포넌트에 대한 private final 필드가 자동으로 생성됩니다.
  • name, price, date를 받아 필드에 값을 할당하는 생성자도 자동으로 생성됩니다.
  • 필드에 접근할 수 있는 name(), price(), date() getter가 자동으로 생성됩니다.
  • equals() / hashCode() / toString() 자동으로 생성됩니다.

 

Record Class 특징

  • 다른 클래스를 상속 받을 수 없습니다.
  • 인터페이스는 구현이 가능합니다.
  • static 필드, 함수, 인스턴스 함수 등을 만들 수 있습니다. 단, 인스턴스 필드는 만들 수 없습니다.
  • 자동 생성되는 메소드들을 직접 재정의 할 수 있습니다.
  • 값을 검증하려면 일반적인 생성자 대신 compact constructor를 사용할 수 있습니다.
public record FruitDtoV2(
    String name,
    int price,
    LocalDate date
) {
  private static final double DISCOUNT_RATE = 0.3;
  
  public int getDiscountPrice() {
    return (int) (price * (1.0 - DISCOUNT_RATE));
  }
  
  // compact constructor
  public FruitDtoV2 {
    System.out.println("생성자 호출!");
    // 값 검증
    if (price < 0) {
      // price = 0; // this를 사용할 수 없습니다. 값을 변경하려면 일반적인 필드를 사용합니다.
      throw new IllegalArgumentException("과일의 가격은 양수입니다!");
    }
  }
  
  public String name() {
    return "[재정의] " + this.name;
  }
}

  compact constructor: 매개변수를 전혀 받지 않아 문법적으로 간결합니다. 단, 필드에 값을 할당할 시 this를 사용할 수 없습니다.

 

Record Class와 스프링 부트

  • Jackson 2.12.0 이상(Spring Boot 2.5.x 이상)에서 Record 클래스 사용이 가능합니다.
  • HTTP 쿼리 파라미터와 바디 모두 잘 적용됩니다.
  • Record 클래스를 사용하면 lombok의 어노테이션 없이 데이터 전달 클래스를 생성할 수 있습니다.
반응형