Kotlin/Class and Interface

[Kotlin] @JvmRecord 사용해 자바의 record 클래스 정의하기

Dev.Cho 2024. 4. 4. 07:52

자바의 Record 클래스

Java 14부터 Record라 불리는 데이터를 저장하는 클래스가 도입됐다. 이 Record는 코틀린의 Data class와 매우 유사한 기능으로, 'record' 키워드로 선언된 클래스는 equals, hashCode, toString 함수를 자동으로 생성한다.

 

예를 들어 다음과 같이 생성된 record 클래스 Blog가 있다고 해보자.

*아래 코드는 Java이다.

public record Blog(String name, int age) { }

 

이 Blog 클래스는 이름(name)과 생긴 후 지난 날짜(age)를 인자로 받는다. 이제 이 record 클래스가 어떻게 동작하는지 확인하기 위해 다음과 같이 코드를 만들어보자.

public class Main {
    public static void main(String[] args) {
        // 레코드 인스턴스 생성
        Blog blog = new Blog("조세영의 Kotlin World", 3);
        Blog _blog = new Blog("조세영의 Kotlin World", 3);

        // toString에 접근
        System.out.println(blog.toString()); // Blog[name=조세영의 Kotlin World, age=3]
        
        // hashCode와 equals 메서드에 접근
        System.out.println(blog.equals(_blog)); // true
    }
}

 

이 코드에서는 Record로 선언된 객체가 어떻게 동작하는지 확인하기 위해 'new Blog("조세영의 Kotlin World", 3)'를 통해 Blog의 인스턴스를 만든 후 toString()을 호출해보며, 이후 equals가 예상대로 동작하는지 확인한다. 이제 코드를 실행해보자. 그러면 다음과 같은 결과가 나오는 것을 볼 수 있다.

 

그림1. record 클래스의 결과

 

toString() 을 호출해보면, 코틀린의 Data class 처럼 toString()이 자동 구현돼 객체가 깔끔하게 출력되는 것을 확인할 수 있고, hashCode와 equals 또한 자동 구현돼, 같은 값을 가진 객체가 동일한 것으로 판단되는 것을 볼 수 있다.

 

자바의 Record 클래스가 Data Class와 다른점 

코틀린의 data class는 그 속성을 가변으로 만들 수 있다. 하지만, record 클래스는 불변임이 강제된다. 즉, 속성의 get 메서드만이 자동 구현되며, set 메서드는 구현되지 않는다. 예를 들어 위의 blog 객체의 age 프로퍼티를 가져오는 것은 age()를 통해 가능하며, 이 값을 변경 할 수는 없다. 

public class Main {
    public static void main(String[] args) {
        // 레코드 인스턴스 생성
        Blog blog = new Blog("조세영의 Kotlin World", 3);

        // toString에 접근
        System.out.println(blog.age()); // 속성에 접근 밖에 안된다.
    }
}

 

또한, record 클래스에서는 copy 메서드가 제공되지 않는다.

 

@JvmRecord 사용해 자바의 record 클래스 정의하기

data class로 선언된 값 객체를 자바의 record 클래스 처럼 선언된 것과 동일하게 만들기 위해 코틀린은 1.5 버전부터 @JvmRecord 어노테이션을 제공한다.

@JvmRecord
data class Blog(val name: String, val age: Int)

 

@JvmRecord 어노테이션을 data class에 붙이면, 이는 자바에서는 record 처럼 인식되며 record에서 생성되는 메서드들이 제공된다. 예를 들어 name()을 통해 name 프로퍼티에 접근하는 것이 포함된다.

public class Main {
    public static void main(String[] args) {
        // 레코드 인스턴스 생성
        Blog blog = new Blog("조세영의 Kotlin World", 3);

        System.out.println(blog.name()); // 속성에 접근 가능
    }
}

 

@JvmRecord를 사용하지 않았을 때와 무엇이 다른가?

위의 Blog 클래스에서 @JvmRecord를 제거해보면 무엇이 다른지가 명확히 드러난다.

data class Blog(val name: String, val age: Int)

 

@JvmRecord를 제거한 Blog 클래스는 name() 을 통해 자바에서 name 프로퍼티에 접근할 수 없다. 대신 일반적인 클래스와 같이 getName 을 통해 프로퍼티에 접근해야 한다.

 

그림2. @JvmRecord가 제거될 때의 접근법

 

반응형