레코드 클래스 정리

이동영·2024년 3월 8일

자바 개념정리

목록 보기
5/21

1.레코드 클래스가 생긴 이유

객체간 불변 데이터를 전달시 자바 14이전에 여러가지 문제가 많은 boilerplate필드 메소드가 있는 클래스를 생성해야만 했다 하지만 자바 14릴리즈 버전 이후 레코드 클래스가 출시 되면서 이러한 문제를 해결할 수 있게 되었다.

정리 : 14이전에는 불변 객체를 생성하기 위해 불필요한 코드를 작성해야 했지만 레코드 클래스가 출시 되면서 간편하게 생성하여 불변 데이터를 전달할 수 있게 되었다.

2.레코드 클래스의 용도

데이터베이스 결과, 서비스 정보등 이러한 데이터를 단순히 보관하기 위해 클래스를 생성해야 하는데 대부분의 경우 불변 데이터이며 이렇게 하는 경우 데이터를 동기화 할 필요가 없이 데이터의 정확성을 보장할 수 있다.

정리 : 객체의 불변성을 보장하기에 정확성이 높아진다.
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를 나타내는 데이터 클래스의 목적이 모호해졌다.

클래스가 데이터 클래스임을 명시하는것이 좋은 방법이다.

요약

  1. 레코드 클래스는 비효율적인 작업을 줄여준다.
  2. 데이터가 목적인 클래스의 의도가 모호해진다.

3.기본사항

  • JDK14부터 반복적인 작업이 들어간 데이터 클래스를 레코드로 대체할 수 있다.
  • 레코드 클래스는 필드의 타입과 이름만 필요한 클래스 이다.
  • equals, hashCode, toString뿐만 아니라 private final 필드와 public생성자를 자바 컴파일러가 제공해준다.

예를들어 레코드 키워드를 사용하여 레코드 클래스를 생성할 수 있다.

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

3-1.생성자

레코드 클래스를 사용하면 각 필드의 인자에 대한 public생성자를 제공해준다.
Person 레코드 클래스는 다음 예제와 같은 생성자를 제공해준다.

public Person(String name, String address) {
    this.name = name;
    this.address = address;
}

생성자는 클래스와 동일한 방식으로 레코드 클래스를 인스던스화 할 수 있다.

Person person = new Person("John Doe", "100 Linda Ln.");

3-2.Getter

또한 이름이 일치한 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());
}

3-3.equals

  • 추가적으로 equals메서드를 제공하며 이 메서드는 객체의 타입과 필드의 값이 모두 일치하면 ture값을 반환한다.
  • 만약 어느 하나라도 다르다면 false를 반환한다.

3-4.hashCode

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());
}
  • 만약 필드의 값이 다르면 해쉬코드의 값도 달라지지만 hashCode 규약에서 명확히 보장되는것은 아니다.

4. Constructors

  • 각 public생성자를 생성할 때 사용자가 생성자 구현부를 정의할 수 있다.
  • 커스터마이징은 유효성 검사에 사용하기 위한것이며 가능한 단순하게 작성해야 한다.
    이 예제는 Person레코드의 필드인 name과 addredss의 값이 null인지 확인하는 기능을 구현한 생성자이다.
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");
    }
}
  • 즉 기본 생성자 이외에 name필드의 값만 받는 생성자를 정의할 수 있으며 주소가 제공되지 않은경우 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;
    }
}

5. static 변수와 static메서드

레코드 클래스도 일반 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.");

6. 결론

레코드 클래스를 사용하면 기본적으로 컴파일러에서 제공되는 메서드와 함께 반복적인 코드를 줄여 불변 클래스의 안정성을 향상시킬 수 있다.

해당 코드는 깃허브에서 참조할 수 있다. https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-14

총정리

  1. 자바14이전에 불변 데이터를 전달하기 위한 클래스를 생성하기 위해 반복적인 프로세스가 들어가야 했다. 하지만 14이후 레코드 클래스가 생기면서 이러한 문제를 해결할 수 있게 되었다.
  2. 디비 결과 여러 정보등을 단순히 보관하고 전달하기 위한 클래스를 생성할 때 레코드 클래스를 사용할 수 있다.
  3. 레코드 클래스는 객체의 불변성을 보장하기에 정확도가 높다.
  4. 각 필드는 private final로 선언이 된다.
  5. class는 final로 선언이 되어 다른곳에서 상속할 수 없다.
  6. 각 필드에 대한 getter를 제공하며 필드명으로 값을 꺼낼 수 있다.
  7. 각 필드에 대한 public생성자를 제공한다.
  8. 모든 필드가 일치하면 true를 반환하는 equals()를 제공한다.
  9. 모든 필드가 일치하면 같은 값의 해시코드를 반환한다.
  10. toStirng()을 기본적으로 제공하여 해당 필드의 이름과 값을 로그에 출력할 수 있다.
  11. 레코드 클래스도 일반 클래스와 마찬가지로 static 필드 메서드를 사용할 수 있으며 일반 클래스와 동일하게 선언한다.
profile
가치를 제공하는 개발자

0개의 댓글