Data Class와 자동 생성 메서드
Data Class와 자동 생성 메서드
코틀린의 data class는 주 생성자(primary constructor)에 선언된 프로퍼티를 기반으로 equals(), hashCode(), toString(), copy(), componentN() 함수를 컴파일러가 자동으로 생성하도록 지시하는 언어 기능입니다. 컴파일러가 어떤 코드를 생성하는지, 어떤 프로퍼티가 생성된 코드에 포함되는지, 그리고 data class가 어떤 상황에서 한계를 보이는지 정확히 이해해야 올바른 도메인 모델을 작성할 수 있습니다. 면접에서도 단순히 "자동으로 메서드를 생성해 줍니다"라는 피상적인 답변을 넘어서, 내부 동작 원리와 함정까지 설명할 수 있어야 좋은 평가를 받을 수 있습니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
- 코틀린 컴파일러가
data class에 대해 생성하는 정확한 메서드 목록과 각 메서드가 참조하는 프로퍼티 범위를 설명할 수 있습니다. equals()와hashCode()가 주 생성자 내부에 선언된 프로퍼티와 외부에 선언된 프로퍼티에 대해 어떻게 다르게 동작하는지 이해할 수 있습니다.copy()함수와 직접 새 인스턴스를 생성하는 방식 사이의 동작 차이를 파악할 수 있습니다.data class가 컬렉션이나 맵에서 미묘한 버그를 유발할 수 있는 상황을 인식할 수 있습니다.- 가변적이거나 동작 중심의 객체를 모델링할 때
data class와 일반 클래스를 비교하여 적절한 선택을 내릴 수 있습니다.
자동 생성 메서드와 주 생성자 프로퍼티
클래스에 data 키워드를 붙이면 컴파일러가 5가지 메서드를 자동으로 생성합니다. 이 메서드들은 모두 주 생성자에 선언된 프로퍼티만을 대상으로 동작한다는 점이 핵심입니다.
data class User(val name: String, val age: Int)
위 선언에 대해 컴파일러는 다음과 같은 메서드를 생성합니다.
equals():name과age를 구조적 동등성(structural equality)으로 비교합니다.hashCode():name과age의 해시 코드를 조합하여 반환합니다.toString():"User(name=..., age=...)"형태의 문자열을 반환합니다.copy(): 특정 프로퍼티만 변경하여 새로운 인스턴스를 생성합니다.component1()과component2(): 구조 분해 선언(destructuring declaration)에 사용됩니다.
클래스 본문(body)에 선언된 프로퍼티는 생성되는 모든 메서드에서 제외됩니다. 이는 버그가 아니라 의도된 설계 결정입니다. 면접에서 이 부분을 명확하게 구분하여 설명할 수 있으면 좋은 인상을 줄 수 있습니다.
data class User(val name: String, val age: Int) {
var email: String = ""
}
val a = User("Alice", 30).apply { email = "a@test.com" }
val b = User("Alice", 30).apply { email = "b@test.com" }
println(a == b) // true, email은 비교 대상에서 제외됨
email 프로퍼티는 equals(), hashCode(), toString(), copy() 어디에도 포함되지 않습니다. 동등성 비교에 참여해야 하는 프로퍼티가 있다면 반드시 주 생성자에 선언해야 합니다.
equals()와 hashCode()의 내부 동작
생성된 equals() 메서드는 먼저 참조적 동등성(referential equality)을 검사하고, 그다음 타입을 검사한 뒤, 마지막으로 각 생성자 프로퍼티를 == 연산자(equals() 호출)를 사용하여 비교합니다. User 클래스의 디컴파일된 바이트코드는 대략 다음과 같은 구조를 갖습니다.