Kotlin의 data class는 왜 상속이 불가능할까?
Kotlin에서 data class는 open으로 선언할 수 없으며, 항상 final로 유지됩니다.
즉, 다른 클래스에서 data class를 상속받는 것은 불가능합니다.
그 이유는 Kotlin의 설계 철학과 data class의 본질적인 목적에 기반하고 있습니다.
Data Class는 자동으로 equals()를 생성한다
실제로 data class는 equals(), hashCode(), toString(), copy() 같은 메서드를 자동으로 생성합니다. 이 메서드들은 해당 클래스의 모든 프로퍼티를 기준으로 동작하죠.
하지만, 만약 data class가 상속이 가능하다면, 부모 클래스에서 구현된 equals()와 자식클래스에서 구현된 equals가 충돌되는 문제가 생깁니다.
예를들어 아래와 같은 코드가 있다고 가정해보겠습니다.
data class Person(val name: String)
data class Student(val name: String, val grade: Int)
이렇게 각각 정의된 두 클래스는 다음과 같은 equals()를 내부적으로 갖고 있다고 볼 수 있어요:
// Person의 equals
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Person) return false
return this.name == other.name
}
// Student의 equals
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Student) return false
return this.name == other.name && this.grade == other.grade
}
상속이 가능하다고 가정해보자
// 이건 실제로는 불가능한 코드! (하지만 가정입니다)
open data class Person(val name: String)
data class Student(val name: String, val grade: Int) : Person(name)
이 상황에서 문제가 되는 건 equals()의 오버라이딩입니다.
상위 클래스 Person도 equals()를 자동 생성하고,
하위 클래스 Student도 자신만의 equals()를 자동 생성합니다.
즉, 두 개의 다른 로직을 가진 equals()가 충돌할 수 있는 문제가 발생합니다.
val person = Person("Alice")
val student = Student("Alice", 3)
println(person == student) // ??? 문제 발생!
- Person 입장에서는 name == "Alice"이므로 같다고 판단합니다.
- Student 입장에서는 name == "Alice"지만 grade == 3이므로, grade가 없으면 다르다고 판단합니다.
이처럼 서로 다른 기준으로 equals()를 판단하게 되면 "동등성의 원칙"이 깨지게 됩니다.
동등성의 원칙이란?
a == b이면 b == a여야 한다.
위 예시에서 보면:
- person == student가 true인데
- student == person은 false일 수 있어요
동등성 판단이 일관되지 않으니, 컴파일러에서 아예 막아버린 거예요.
data 클래스를 상속하지 않고 비슷한 구조를 만들 수 있을까?
어떻게든 Data Class를 다른 Data Class에서 상속받아 사용하고 싶을 수 있습니다.
이 때는 두 가지 선택지가 있는데, 하나는 추상 클래스이고 다른 하나는 인터페이스를 사용하는 것입니다.
1. interface 사용
가장 깔끔하고 안전한 방법입니다. 공통 속성이나 기능이 있다면 interface로 추출해서 각 data class가 구현하도록 만들 수 있어요.
interface Person {
val name: String
}
data class Student(
override val name: String,
val grade: Int
) : Person
data class Teacher(
override val name: String,
val subject: String
) : Person
- data class는 여전히 그대로 사용 가능 (equals, copy, 등 유지)
- 공통 인터페이스를 통해 다형성 활용 가능
2. 추상 클래스 (abstract class)
abstract class Person(
open val name: String
)
data class Student(
override val name: String,
val grade: Int
) : Person(name)
- data class는 여전히 그대로 사용 가능 (equals, copy, 등 유지)
- 공통 속성을 추상 클래스에서 정의할 수 있어 중복 제거
결론
상속을 허용하면 상하위 클래스 간 equals(), hashCode() 등 같은 메서드가 서로 충돌하거나, 일관되지 않게 작동할 위험이 있습니다.
그래서 코틀린은 data class 간 상속을 명시적으로 금지한 거예요.
대신, interface 또는 abstract class를 활용하면 안전하면서도 유사한 구조를 구성할 수 있습니다.
참고자료
https://minkukjo.github.io/language/2020/06/01/Kotlin-13/
Kotlin Data Class는 왜 상속이 안될까?
Kotlin Data Class
minkukjo.github.io
https://velog.io/@trasalby/Data-Class
Data Class
데이터를 다루는데 최적화된 형태로 코틀린에서 제공하는 클래스형태이다.데이터 클래스는 아래의 요건을 충족하여야 한다.기본 생성자는 1개 이상의 매개변수를 가져야 한다.모든 기본 생성
velog.io
'Kotlin' 카테고리의 다른 글
[Kotlin] 인라인(Inline) 함수란? (0) | 2025.04.02 |
---|---|
[Kotlin] Nested Class와 Inner Class (0) | 2025.02.27 |
[Kotlin] 코틀린(Kotlin)에서 동시성 문제를 해결하는 방법 (0) | 2025.02.13 |
[Kotlin] 불변성(Immutable)과 가변성(Mutable) (5) | 2024.11.07 |
[Kotlin] 확장(Extension) 함수 (5) | 2024.11.06 |