Java

[Java] Record(레코드)

pseudocoder_ 2024. 4. 6. 16:22
728x90

Class vs Record

자바의 Record는 Java 14부터 도입된 순수하게 데이터를 저장/관리하기 위한 클래스이다.

 

아래의 예시를 살펴보자. immutable 한 객체 Person을 생성하고 관리하는 코드이다.

객체의 불변성과 유효성을 보장하기 위해서 equals(), hashCode(), toString() 등의 메서드를 오버라이드하고 있으며, 접근제한자 private, 키워드 final을 사용하고 있다.

import java.util.Objects;

public class RecordTest {
    public static void main(String[] args) {
        Person person = new Person("John Doe", "test address");

        System.out.println("hash: " + person.hashCode());
        System.out.println("toString: " + person.toString());
        System.out.println("equals: " + person.equals(new Person("John Doe", "test address")));
    }

    public static 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) {
            // 같으면 true
            if (this == obj) {
                return true;
            }
            // 같은 타입의 객체가 아니라면 false
            else if (!(obj instanceof Person other)) {
                return false;
            }
            // 객체의 필드 값 비교
            else {
                return Objects.equals(name, other.name) &&
                        Objects.equals(address, other.address);
            }
        }

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

 

Record를 사용하면 매번 클래스를 생성할 때마다 메서드 오버라이딩 하고 접근제한자를 지정할 필요가 없다.

긴 코드로 작성하던 것을 단 한 줄의 코드로 줄일 수 있다.

필드를 자동으로 private final로 선언해 주고 생성자, hashCode(), equals(), toString(), Getter까지 자동으로 생성해 준다.

public class RecordTest {
    public static void main(String[] args) {
        Person person = new Person("John Doe", "test address");

        System.out.println("hash: " + person.hashCode());
        System.out.println("toString: " + person.toString());
        System.out.println("equals: " + person.equals(new Person("John Doe", "test address")));
    }

    public record Person(String name, String address) { }
}

같은 결과가 출력되는 것을 확인할 수 있다.

 

이러한 장점 덕분에, 데이터의 유효성이 중요한 Entity나 DTO, VO에 자주 사용한다.

 

 

Record의 Getters

일반적으로 객체의 getter 선언 시, get필드명()으로 선언하지만 Record에서는 필드명() 메서드로 Getter를 자동으로 생성해 준다.

Person person = new Person("John Doe", "test address");

String name = "John Doe";
String address = "test address";

System.out.println(person.name().equals(name));
System.out.println(person.address().equals(address));

 

record를 주제로 스터디를 하다가 Record의 getter에 대해서 궁금증이 하나 생겼다.

 

@Getter 어노테이션을 사용해서 get변수명()을 getter 컨벤션으로 사용하는 프로젝트의 경우, record를 도입하려고 할 경우에는 어떻게 해야 할까에 대한 의문점이 생겼다. record의 getter는 Record.변수명() 이기 때문에 코드 일관성을 해치고 컨벤션을 위반하게 된다. 객체를 record화 하되 기존의 getter 컨벤션을 적용하는 방법이 없을까?

 

이에 대한 해답을 찾았는데, 아래와 같은 방법으로 가능하다.

public record MyObject(@Getter int id) {
}

 

해당 방법을 사용할 경우 Record.변수명(), get변수명() 두 가지 방법으로 Getter를 사용할 수 있게 된다. 이 방법이 좋은지에 대해서는 갑을론박이 있을 수는 있지만 이러한 방법도 있다는 것을 알아보았다.

 

Record의 Constructor

Record의 public 생성자 외에 유효성 검증을 위한 생성자 선언이 가능하다.

아래는 필드 값으로 Null이 들어오는 것을 방지하기 위한 생성자이다.

import java.util.Objects;

public class RecordTest {
    public static void main(String[] args) {
        Person person1 = new Person(null, null);
    }

    public record Person(String name, String address) {
        public Person {
            Objects.requireNonNull(name);
            Objects.requireNonNull(address);
        }
    }
}

NPE가 발생하는 것을 확인할 수 있다.

 

 

public 생성자와 다른 인자를 받는 생성자 선언도 아래처럼 가능하다.

import java.util.Objects;

public class RecordTest {
    public static void main(String[] args) {
        Person person = new Person("Jane Doe");
        System.out.println(person.name());
        System.out.println(person.address());
    }

    public record Person(String name, String address) {
        public Person {
            Objects.requireNonNull(name);
            Objects.requireNonNull(address);
        }

        public Person(String name) {
            this(name, "default");
        }
    }
}

 

 

record 선언 시 기본 public 생성자와 같은 생성자를 선언하면 컴파일 에러가 뜨니 참고하자.

 

 

Record의 static 변수/메서드 선언

record에서의 static 변수와 static 메서드 선언 및 활용은 다음과 같다.

public class RecordTest {
    public static void main(String[] args) {
        // record명을 사용해서 static 변수와 static 메서드 모두 호출 가능하다.
        System.out.println(Person.unnamed(Person.UNKNOWN_ADDRESS));
    }

    public record Person(String name, String address) {

        // static 변수 선언
        public static String UNKNOWN_ADDRESS = "unknown address";
        // static 메서드 선언
        public static Person unnamed(String address) {
            return new Person("Unnamed", address);
        }
    }
}

 

 

References

https://gyoogle.dev/blog/computer-language/Java/Record.html

 

[Java] Record | 👨🏻‍💻 Tech Interview

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

gyoogle.dev

https://www.baeldung.com/java-record-keyword

https://github.com/projectlombok/lombok/issues/3058

 

Add the possibility to use @Getter on java 17 record on the different components. · Issue #3058 · projectlombok/lombok

Add the possibility to use @getter on java 17 record on the different components. The reason why is we don't know when you use a class if it is a record or a class and the way to retrieve attribute...

github.com

 

728x90