Please enable JavaScript to view the comments powered by Disqus.Java 14 Record keyword 는 무엇인가
Search

Java 14 Record keyword 는 무엇인가

태그
Java
공개여부
작성일자
2022/09/08
@ConfigurationProperties 에 관련된 글을 정리하다가 알게된 Java 14 의 records 의 기능에 대해 정리해 본다. 이걸 보니 자바 최신 버젼을 쓰고싶다 하앍
개요

Introduction

자바에서 객체간 immutable 객체를 전달하는 것은 그리 대단한일이 아니며 상당히 평범하고 자주 있는 일이다.
자바 14 이전에는 자주 사용되고, 반복되는 필드 및 method 들을 직접 만들어야 했으며 이것은 실수 혹은 장애로 이어질 수 있었다.
하지만 자바 14에선 Record 를 사용하여 이러한 문제를 해결할 수 있었다.

목적

공통적으로 database result, query result 등의 정보를 서비스할 때 데이터를 저장하기 위한 class를 만들었다.
많은 상황에서 이러한 데이터는 불변(immutable)을 유지하는 것이 유용하다.
왜냐하면 immutable 은 동기화 없이 데이터의 유효성을 보장하기 때문이다.
이러한 데이터 유효성을 얻기 위해 data class 는 다음의 특징을 구현해야 한다.
1.
private, final 을 모든 필드에 사용한다.
2.
각각의 필드에 대해 getter 를 구현한다.
3.
public constructor 를 각각의 필드를 파라미터로 받도록 만들어야 한다.
4.
equals 는 모든 필드가 동일할 때만 true 를 반환하도록 구현해야 한다.
5.
hashCode 는 모든 필드가 동일하면 같은 값을 반환해야 한다.
6.
toString 은 class 의 이름, field 의 이름, 해당 field 에 대응하는 value 으로 구성되어야 한다.
예를 들어 Person 클래스를 다음과 같이 정의한다.
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 }
Java
복사
그런데 immutable 의 강점을 얻기 위해 다음의 두 가지 문제가 존재한다.
1.
Boilerplate 에 해당하는 코드가 너무 많다.
2.
name, address 필드를 가진 Person 의 목적이 애매해진다.
첫 번째 경우에 대해서는 무의미한 반복이며 이것을 개발이라고 하기엔 너무 별거 아니다. 매번 새로운 필드가 추가될 때 마다 equals, hashCode, toString 함수도 추가된 필드가 반영되어야 한다.
IDE 에서 이러한 무의미한 반복을 대신 해결해주기도 하지만, 새로운 필드가 추가되었을때도 알아서 자동으로 equals 를 업데이트해주거나 하진 않는다.
그리고 추가된 함수들은 이 간단한 data class 를 불문명하게 만들기 때문에 더 나은 접근의 필요성이 발견되었다.

The Basics

JDK 14 에서 이러한 반복을 records 를 통해서 개선하였다.
Records 는 immutable data class 로 오로지 type 과 name field 만 필요하다.
즉, equals, hashCode, toString, private, final 등의 무의미한 반복이 필요하지 않은 것이다.
Person record 는 다음과 같이 정의한다.
public record Person (String name, String address) {}
Java
복사
이와 같이 정의한다면, 각 필드는 실제로 final 을 사용해 immutable 이 된다.
이때 두 필드에 접근하는 것은 name(), address() 가 된다.

Constructor

Records 를 사용한다면, public constructor 가 필요하고, 각각의 필드를 argument 로 정의한다.
public Person(String name, String address) { this.name = name; this.address = address; }
Java
복사
이 constructor 를 통해서 instance 를 생성하는 방법은 다음과 같다.
Person person = new Person("Josh Park", "woorizip, seoul");
Java
복사

Getters

기존의 immutable class 와 비슷하게 getter 역시 제공하며 이를 위하 또다른 노력을 할 필요는 없다.
이 getter 의 이름은 field 의 이름과 동일하며 method 로 호출하면 된다.
Kotlin 의 간편함이 java 로 전달된 것 같다.
@Test public void givenValidNameAndAddress_whenGetNameAndAddress_thenExpectedValuesReturned() { String name = "Josh Park"; String address = "woorizip, seoul"; Person person = new Person(name, address); assertEquals(name, person.name()); assertEquals(address, person.address()); }
Java
복사

equals

equals 함수 또한 자동으로 생성된다.
만약 이 equals 가 true 로 반환 되었다면 다음의 조건이 and 로 만족하는 것이다.
1.
Object type 이 일치한다.
2.
모든 필드가 match 된다.
3.
모든 필드의 value 가 일치한다.
@Test public void givenSameNameAndAddress_whenEquals_thenPersonsEqual() { String name = "John Doe"; String address = "100 Linda Ln."; Person person1 = new Person(name, address); Person person2 = new Person(name, address); assertTrue(person1.equals(person2)); }
Java
복사
위 3가지 조건중 하나라도 만족하지 않는다면, false 를 반환한다.

hashCode

hash code 에 대해서도 나중에 다뤄야겠지만, hashCodeequals 와 대응된다.
만약에 hashCode 가 동일한 값을 반환한다면 equals 의 3가지 조건이 동시에 만족하는 역할을 계승한다.
@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()); }
Java
복사

toString

마지막으로 toString 함수는 record 의 이름, 각 필드의 이름, 그리고 필드에 대응되는 value 가 대괄호를 사용해 String 으로 반한된다.
Person[name=John Doe, address=100 Linda Ln.]
Java
복사

Constructors

Constructor 가 생성된다면, 역시 constructor 를 customize 하여 구현할 수 있다.
예를 들면, name, addressnonNull 을 만족해야 한다고 가정하자, 그러면 다음과 같이 constructor 를 정의할 수 있다.
public record Person(String name, String address) { public Person { Objects.requireNonNull(name); Objects.requireNonNull(address); } }
Java
복사
이를 Compact constructor 라 한다.
이 덕분에 기존의 Java class 처럼 constructor 의 argurment list 를 구구절절히 작성할 필요가 없다.
또한, 새로운 constructor 를 정의해서 argument list 를 변경하는 방법도 가능하다.
public record Person(String name, String address) { public Person(String name) { this(name, "Unknown"); } }
Java
복사
보통의 class와 같이 field 는 this keyword 를 사용하여 접근할 수 있고, argument 를 해당하는 필드에 매칭시키면 된다.
알아두어야 할 점은, 자동 생성되는 constructor 와 동일한 argument 의 constructor 를 정의한다면, 각각의 필드를 직접 초기화 해줘야 한다.
public record Person(String name, String address) { public Person(String name, String address) { this.name = name; this.address = address; } }
Java
복사
추가적으로, argument 가 없는 생성자와 자동으로 생성된 constructor 와 일치하는 argument를 갖는 생성자를 정의한다면, compile error 가 발생한다.
따라서, 다음의 코드는 compile 이 되지 않는다.
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 variables & Methods

일반적인 Java class 와 같이, static variable 와 static method 를 records 안에 정의할 수 있다.
이는 기존의 Java class 와 동일한 syntax 를 갖는다.
public record Person(String name, String address) { public static String UNKNOWN_ADDRESS = "Unknown"; }
Java
복사
public record Person(String name, String address) { public static Person unnamed(String address) { return new Person("Unnamed", address); } }
Java
복사
또한, static 변수와 static method 는 record 의 이름에 instance 생성 없이 바로 호출할 수 있다.
Person.UNKNOWN_ADDRESS Person.unnamed("woorizip, seoul");
Java
복사

Records 의 제한사항

Records 는 다른 클래스를 상속받는 것이 불가능하다.
abstract class 로 정의하는 것도 불가능하다.
final 임을 상기하자
Records 의 component 는 절대적으로 final 이다.
Instance field 를 선언할 수 없다.
Static 을 제외한 다른 field 는 선언할 수 없음

결론

이 글은 Java 14에 추가된 records 의 기초와 컨셉을 소개하는 글이다.
Records 는 compiler 에서 자동으로 method 를 생성해주고, 이는 boilerplate 에 해당하는 getter, equals, hashCode 등을 제거하며 immutable class의 신뢰성을 높힐 수 있다.