티스토리 뷰

Programming Language/Java

[Java] record

SdardewValley 2022. 6. 25. 21:57
반응형

record의 도입

  record는 Java 14 버전부터 새롭게 도입된 키워드이다. enum과 마찬가지로 record도 제약이 있는 클래스의 한 종류이다. 데이터를 변경하지 않고 생성자와 접근자 메서드를 포함하는 plain data carrier라고 오라클 공식문서에서 설명한다.

  Spring의 DTO와 같이 객체 간에 불변 데이터를 전달하는 일은 빈번한다. 불변 데이터를 전달하기 위해 사용하는 DTO 객체의 경우에는 setter 메서드 같은 데이터를 변경하는 메서드를 작성하지 않고 생성자로 인스턴스가 생성될 때만 값을 초기화하고 필드 앞에 final을 붙이는 방식으로 구현했었다. 이는 record를 통해서 대체할 수 있다.

 

 

record를 class로 변환

public record Rectangle(float length, float width) {
}

  위의 코드는 간단한 레코드 클래스이다. 인텔리제이에서 [option] + [Enter]로 레코드를 클래스로 변환할 수 있는데, 변환 결과는 아래와 같다.

 

import java.util.Objects;

public final class Rectangle {
	private final float length;
	private final float width;

	public Rectangle(float length, float width) {
		this.length = length;
		this.width = width;
	}

	public float length() {
		return length;
	}

	public float width() {
		return width;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (obj == null || obj.getClass() != this.getClass())
			return false;
		var that = (Rectangle)obj;
		return Float.floatToIntBits(this.length) == Float.floatToIntBits(that.length) &&
			Float.floatToIntBits(this.width) == Float.floatToIntBits(that.width);
	}

	@Override
	public int hashCode() {
		return Objects.hash(length, width);
	}

	@Override
	public String toString() {
		return "Rectangle[" +
			"length=" + length + ", " +
			"width=" + width + ']';
	}

}

java 코드를 정리하면 아래와 같다.

 

  1. final 클래스
  2. private 접근 권한을 가지고 final이 붙은 수정 불가능한 필드
  3. 모든 필드를 인자로 받는 생성자
  4. 메서드명과 같은 필드명을 가진 필드를 반환하는 접근자
  5. 모든 필드 값을 비교해주는 equals 메서드
  6. 해시값을 반환하는 hashCode 메서드
  7. 클래스 명과 필드의 값을 보여리턴하는 toString 메서드

 

 

record의 특징

record는 필드의 타입과 필드의 이름만을 요구하는 불변 데이터 클래스이다.

  자바 컴파일러에 의해 private final 필드, 모든 필드에 대한 인자가 있는 public 생성자, equals 메서드, hashCode 메서드, toString 메서드가 자동으로 생성된다.

 

public record Rectangle(int height, int width) {}

  위의 간단한 record와 테스트를 통해서 record의 특징을 살펴보겠다.

 

모든 필드를 인자로 가지는 public 생성자

@Test
@DisplayName("record는 public 생성자를 가진다")
void publicConstructor() {
    Rectangle rectangle = new Rectangle(3, 4);
}

  record를 통해서 인스턴스를 생성하는 방식은 class와 동일하다. 단, 모든 필드의 값을 채워줘야 한다.

 

필드명과 동일한 이름의 접근자

@Test
@DisplayName("record는 필드명과 동일한 접근자를 가진다")
void getter() {
	Rectangle rectangle = new Rectangle(3, 4);

	assertAll(
		() -> assertEquals(rectangle.height(), 3),
		() -> assertEquals(rectangle.width(), 4)
	);
}

  record는 접근자도 자동으로 생성해준다. 기존에 getXxx()와 같은 형태의 메서드를 정의해서 필드에 접근했지만,  레코드에서는 필드명과 동일한 메서드명으로 필드의 값을 읽을 수 있다. Rectangle 레코드의 필드에는 height와 width가 있었다. 각각 height(), width() 메서드를 통해서 필드 값에 접근할 수 있다.

 

equals

@Test
@DisplayName("record는 equals 메서드를 자동으로 생성한다")
void equals() {
	Rectangle rectangle1 = new Rectangle(3, 4);
	Rectangle rectangle2 = new Rectangle(3, 4);
	
	assertEquals(rectangle1, rectangle2);
}

assertEquals 메서드의

  assertEquals를 내부구현을 살펴보면 equals 메서드를 통해서 비교한다. record에서 필드의 값들을 비교해주는 equals 메서드를 생성해주기 때문에 rectangle1, rectangle2는 assertEquals 테스트를 통과한다.

 

hashCode

@Test
@DisplayName("record는 hashCode 메서드를 자동으로 생성한다")
void hashcode() {
	Rectangle rectangle1 = new Rectangle(3, 4);
	Rectangle rectangle2 = new Rectangle(3, 4);

	assertEquals(rectangle1.hashCode(), rectangle2.hashCode());
}

  record는 hashCode 메서드도 자동으로 생성을 한다. 비교하는 인스턴스들이 같은 데이터 값을 가지고 있기 때문에 위 코드의 테스트도 통과한다.

 

toString

@Test
@DisplayName("record는 toString 메서드를 자동으로 생성한다")
void tostring() {
	Rectangle rectangle = new Rectangle(3, 4);
	System.out.println(rectangle);
}

테스트 출력 결과

  record는 toString 메서드를 자동으로 생성하여 클래스명과 필드명 그리고 필드의 값들을 모두 출력해준다.

 

 

생성자 정의

public record Rectangle(int height, int width) {
	public Rectangle(int sameHeightAndWidth) {
		this(sameHeightAndWidth, sameHeightAndWidth);
	}

	public Rectangle(int height, int width) {
		if(height <= 0 || width <= 0) {
			throw new IllegalArgumentException();
		}
		this.height = height;
		this.width = width;
	}
}

  record에서 생성자를 자동으로 생성해주기는 하지만, 따로 생성자를 만들 수도 있다. 위의 코드를 보면 여전히 모든 필드들에 값을 지정하고 있다. 필드들은 기본적으로 final 키워드를 가지고 있기 때문에 만약 한 개의 필드라도 값을 초기화하지 않는다면 컴파일 에러가 발생한다.

 

 

static field와 static method

public record Rectangle(int height, int width) {
	public static int MAX_HEIGHT = 100;
	public static int MAX_WIDTH = 100;

	public static Rectangle maxRectangle() {
		return new Rectangle(MAX_HEIGHT, MAX_WIDTH);
	}
}

  record는 static 필드와 메서드를 가질 수 있다. 클래스와 static 필드와 메서드 동일한 방식으로 사용하면 된다.

 

 

결론

  record를 사용하면 자동으로 코드를 생성해주기 때문에 작성할 코드를 줄일 수 있다. 그리고 값이 변경되지 않기 때문에 안전하기도 하다.

  record를 java 코드로 변환하면 final class이다. 따라서 record 상속이 불가능하고, 더 이상 추가가 되지 않는 최종적인 클래스이기도 하다.

 

출처

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함