Development/Code

Java 21의 레코드 패턴으로 클린 코드 작성하기

Danny Seo 2024. 7. 1. 19:14

Java 21 : 레코드 패턴으로 클린 코드 작성하기

Java는 Java 14에서 레코드 타입(Record Type)을 도입했습니다. Java 21 버전에서는 레코드 클래스의 인스턴스를 분해하고 더 정교한 데이터 쿼리를 가능하게 하는 레코드 패턴(Record Patterns)이 도입되었습니다.

레코드 타입이란 무엇인가?

Java는 Java 14에서 레코드라는 특별한 타입을 도입했습니다.

레코드는 제한된 형태의 클래스 타입으로, 주로 데이터를 저장하는 목적으로 사용됩니다. 레코드는 클래스보다 간결합니다.

 

레코드 타입 정의의 예시입니다:

// 레코드 타입 정의
record Point(int x, int y)

 

위 코드는 아래와 같은 기능을 수행합니다:

  • Point라는 레코드 타입을 선언합니다.
  • 변수 xy에 대한 private final 필드를 포함합니다.
  • x()y()라는 메소드 이름으로 int 타입의 접근자 메소드를 포함합니다.
  • xy 멤버를 가진 public 생성자를 제공합니다.
  • equals(), hashCode(), toString() 메소드의 구현을 포함합니다.

Java 21에서 도입된 레코드 패턴

이 글에서는 Java 21에서 도입된 레코드 패턴에 대해 논의할 것입니다. 레코드 패턴은 JEP 405와 함께 Java 19에서 미리보기 기능으로 처음 도입되었으며, JEP 432를 통해 Java 20에서 추가로 개선되었습니다. Java 21은 JEP 440을 통해 이 기능에 몇 가지 주요 변화를 가져왔습니다.

 

레코드 패턴의 주요 목적은 패턴 매칭을 확장하여 레코드 인스턴스를 분해하는 것입니다. 이를 통해 레코드 인스턴스에 대한 더 간결하고 정교한 데이터 쿼리를 작성할 수 있습니다. 레코드 패턴은 또한 중첩 패턴을 지원하여 더 구성 가능한 데이터 쿼리를 형성할 수 있습니다.

Java 16의 instanceof 연산자 개선

Java 16에서는 패턴 매칭을 허용하는 타입 패턴을 instanceof 연산자에 도입했습니다:

// Java 16 이전
public void sayHello(Object message) {
  if (message instanceof String) {
    String messageStr = (String) message;
    System.out.println(messageStr);
  }
}

// Java 21 이후
public void sayHello(Object message) {
  if (message instanceof String messageStr) {
    System.out.println(messageStr);
  }
}

 

위 코드에서, messageString 타입인 경우 instanceof 평가가 참이 되고, messageStr 변수는 message의 값으로 초기화됩니다. 이는 Java 16 코드에서 추가 캐스팅이 필요했던 것을 없앴습니다.

레코드와 레코드 패턴

레코드는 데이터를 저장하는 데 사용됩니다. 주된 목적은 POJO 클래스의 장황함을 줄이는 것입니다. 레코드는 레코드 변수에 대한 접근자 메소드를 제공하여 쉽게 접근할 수 있게 합니다. Java 21은 분해(de-structuring)를 통해 레코드 변수를 접근하는 방법을 단순화합니다.

 

예시를 통해 설명해보겠습니다 : 

record Point(int x, int y) {}

// Java 16 사용
void print(Object object) {
  if (object instanceof Point p) {
    int x = p.x();
    int y = p.y();
    System.out.printf("X is %d, y is %d%n", x, y);
  }
}

// Java 21 사용
void print(Object object) {
  if (object instanceof Point(int x, int y)) {
    System.out.printf("X is %d, y is %d%n", x, y);
  }
}

 

Java 16을 사용할 때는 레코드 접근자 메소드를 사용하여 변수를 얻어야 했습니다. 그러나 Java 21에서는 패턴 매칭이 객체를 Point 타입으로 내부적으로 캐스팅할 뿐만 아니라, 변수를 분해하여 사용할 수 있게 합니다. 위의 예시는 간단한 레코드 분해를 보여줍니다.

 

또, 우리는 중첩된 레코드 패턴으로 이 분해를 확장할 수 있습니다.

예시를 통해 이를 살펴보겠습니다 :

// 레코드 타입 Point 정의
record Point(int x, int y) {}

// enum 타입 Color 정의
enum Color { RED, GREEN, BLUE }

// ColoredPoint 정의
record ColoredPoint(Point p, Color c) {}

// 특정 x-y 평면상의 점을 정의하는 RandomPoint
record RandomPoint(ColoredPoint cp) {}

 

이제 RandomPoint 인스턴스를 정의하고 점의 멤버 변수를 접근해봅시다 :

// 레코드 인스턴스화
Point p = new Point(1, 2);
ColoredPoint coloredPoint = new ColoredPoint(p, Color.RED);
RandomPoint randomPoint = new RandomPoint(coloredPoint);

// 중첩된 레코드 패턴을 사용하여 Point 변수 접근
void nestedPrint(Object object) {
  if (object instanceof RandomPoint(ColoredPoint(Point(int x, int y), Color c))) {
    System.out.printf("X is %d, y is %d, Color is %s%n", x, y, c.name());
  }
}

// nestedPrint(randomPoint)
// 출력: X is 1, y is 2, Color is RED

 

위 코드에서 중첩된 분해가 if 구문 안에서 어떻게 이루어지는지 주목하세요.

 

이 글에서는 Java 21의 레코드 패턴 사용법을 탐구했습니다.

먼저 레코드 타입에 대해 논의한 후 레코드 패턴을 소개했습니다. 마지막으로 예제를 통해 중첩된 레코드 패턴을 탐구해 보았습니다.

 

끝까지 읽어주셔서 정말 감사합니다.

즐거운 하루 되세요 ㅎㅎ (_ _)