자바

java 21 - Record Patterns 소개

알쓸개잡 2024. 3. 20.

Java의 pattern matching 도입 이후 구문 단순화와 코드 가독성 및 안전성 향상을 목표로 점진적으로 발전해 왔다.

switch 표현식에서의 pattern matching, instanceof에 대한 pattern matching이 대표적이다.

이번 포스팅에서는 이전 버전의 preview 기능을 거쳐 java 21에서 정식 릴리즈된 Record Patterns에 대해서 소개하고자 한다.

이전에 다뤘던 pattern matching에 대해서는 아래 포스팅에 정리해 보았다.

2023.08.26 - [자바] - jdk pattern matching for switch

 

jdk pattern matching for switch

jdk 17 이전 릴리즈 에서는 switch 문의 선택자 표현식은 숫자, 문자열, Enum 상수로만 평가되었고 case 레이블은 상수만 지원을 하였습니다. jdk 17 릴리즈 부터는 switch 문의 선택자 표현식은 모든 타입

devel-repository.tistory.com

2023.08.26 - [자바] - jdk pattern matching for instanceof

 

jdk pattern matching for instanceof

pattern matching 은 객체가 특정 구조를 가지고 있는지 테스트한 다음 일치하는 경우 해당 객체에서 데이터를 추출하는 작업을 포함합니다. 이번 포스팅에서는 instanceof 연사자에 pattern matching 을 적

devel-repository.tistory.com

더불어 Record 클래스 타입에 대한 소개는 아래 포스팅에 정리해 보았다.

2023.08.19 - [자바] - java record 용법 - from jdk 14

 

java record 용법 - from jdk 14

jdk 14 에서 새로운 타입 으로 record 가 preview 형태로 도입되었습니다. Enum 타입과 마찬가지로 record 타입은 몇가지 제약사항을 가지고 있으며 불변의 데이터 셋을 정의하는데 상당한 간소함을 제공

devel-repository.tistory.com

 

Record 패턴은 Record 클래스 유형의 인스턴스인지 테스트하고 (레코드 클래스 참조), 인스턴스인 경우 구성 요소 값에 대해서 재귀적으로 패턴 일치를 수행할 수 있게 한다.

참고로 Record 패턴의 도입 배경은  JEP440 에서 확인해 볼 수 있다.

 

일반 용법

아래 샘플 코드는 인자로 전달된 obj 인스턴스가 Point 레코드의 인스턴지 여부를  Record 패턴과 함께 테스트한다.

record Point(double x, double y) {
    static void checkPattern(Object obj) {
        if ( obj instanceof Point(double x, double y) ) {
            System.out.println("x: " + x + ", y: " + y);
        }
    }
}

=====================================

Point point = new Point( 1.0, 2.0 );
Point.checkPattern( point );

======================================

출력
Point x: 1.0, y: 2.0

샘플코드에서 알 수 있듯이 Record 패턴을 통해서 if문 내부에서 obj 인스턴스에서 x, y 값을 직접 추출하여 Point레코드의 접근자 메서드를 자동으로 호출하게 된다.

 

Record 패턴은 타입과 Record 패턴 목록으로 구성된다. 위 예시 코드에서 타입은 Point이고 패턴 목록은 (double x, double y)다.

체크하는 인스턴스가 null인 경우 어떠한 레코드 패턴과도 일치하지 않는다.

 

다음과 같이 Record 패턴 대신에 타입 패턴을 사용할 수 있다.

static void checkType(Object obj) {
    if ( obj instanceof Point p ) {
        System.out.println("Point x: " + p.x + ", y: " + p.y);
    }
}

타입 패턴을 사용하는 경우에는 Record 패턴과 달리 x, y를 직접 호출하여 access 해야 한다.

 

 

Generic Records

Record 클래스가 Generic인 경우 Record 패턴에서 타입 인수를 명시적으로 지정할 수 있다.

record ValueRecord<T>(T t) {
    static void checkTypeAndPrintValue(ValueRecord<String> obj) {
        if ( obj instanceof ValueRecord<String>(String s) ) {
            System.out.println("String ValueRecord: " + s);
        }
    }
}

===========================================

ValueRecord<String> valueRecord = new ValueRecord<>( "generic record" );
ValueRecord.checkTypeAndPrintValue( valueRecord );

===========================================

출력
String ValueRecord: generic record

checkTypeAndPrintValue 메서드의 파라미터에 Generic 타입이 명시적으로 지정되지 않은 경우에는 type cast 관련 오류를 만날 수 있다.

static void notCheckTypeAndPrintValue(ValueRecord obj) {
    // error : ValueRecord cannot be safely cast to ValueRecord<String>
    if ( obj instanceof ValueRecord<String> v ) {
        System.out.println("ValueRecord: " + v.t);
    }
}

static void notCheckTypeAndPrintValue(ValueRecord obj) {
    // no error
    if ( obj instanceof ValueRecord<?> v ) {
        System.out.println("ValueRecord: " + v.t);
    }
}

 

 

타입 추론

Record 패턴의 구성 요소 목록에서 var를 사용할 수 있다.

static void checkPattern(Object obj) {
    if ( obj instanceof Point(var x, var y) ) {
        System.out.println("Point x: " + x + ", y: " + y);
    }
    else {
        System.out.println("not Point record type");
    }
}

위 코드에서 컴파일러는 변수 x, y는 double로 타입 추론을 한다.

컴파일러는 패턴을 허용하는 모든 구조체(switch 문, switch 표현식, instanceof 표현식)에서 레코드 패턴의 타입을 추론할 수 있다.

 

 

Nested Record Patterns

중첩된 Record 패턴을 사용할 수 있다.

record NestedRecord(Point p1, Point p2) {
    static void checkPattern(Object obj) {
        if ( obj instanceof NestedRecord(Point(var x, double y), var p2) ) {
            System.out.println("Point1 x: " + x + ", y: " + y);
            System.out.println("Point2 x: " + p2.x + ", y: " + p2.y);
        }
        else {
            System.out.println("not Point record type");
        }
    }
}

========================================

Point p1 = new Point( 1.0, 2.0 );
Point p2 = new Point( 3.0, 4.0 );
NestedRecord nestedRecord = new NestedRecord( p1, p2 );
NestedRecord.checkPattern( nestedRecord );

========================================

출력
Point1 x: 1.0, y: 2.0
Point2 x: 3.0, y: 4.0

 

Record 패턴은 기존 패턴 일치 기능에 원활하게 통합되어 레코드 해체를 단순화할 뿐 아니라 코드 가독성과 유지 관리성을 향상한다.

 

끝.

댓글

💲 추천 글