데이터를 다루는데 최적화된 형태로 코틀린에서 제공하는 클래스형태이다.
데이터 클래스는 아래의 요건을 충족하여야 한다.
위의 요건에 따라 데이터 클래스는 상속을 받을 수 없는데 그 이유는 다음과 같다.
만약 상속이 꼭 필요하다면 추상 클래스와 인터페이스등을 활용하여 Data Class가 각각의 함수를 구현할 수 있도록 하여 구현할 수 있다.
abstract class Base(open val data1:String)
data classA(override val data1:String):Base(data1)
data classB(override val data1:String, val data2:String):Base(data1)
equals(), hashCode(), toString(), copy(), componentN() 5가지의 유용한 함수를 내부에서 자동적으로 생성해 준다.
Data Class에서는 코딩하는 과정에서 캡슐화를 위해 필수적 이지만 직접 생성하기에는 번거로운 과정들을 내부에서 함수로 처리하여 직접 구현하지 않고 바로 사용할 수 있다.
참조 동일성이 아닌 객체의 동등성을 검사
- 코틀린의 구조적 동등 연산자인 “==”을 사용해 호출할 수 있다.
- a.equals(b) 는 a==b와 같은 동작을 수행한다.
- 참조 동일성을 비교하기 위해서는 “===”을 사용할 수 있다.
- 주 생성자가 아닌 프로퍼티의 값은 다르더라도 생성자의 값만 비교하여 같은지 확인한다.
fun main() {
val user1 = User("이호성")
val user2 = User("이호성")
println("user1 : $user1")
println("user2 : $user2")
println("user1과 user2는 같다? ${user1 == user2}")
user2.age+=1
println("user1 : $user1")
println("user2 : $user2")
println("user1과 user2는 같다? ${user1 == user2}")
}
data class User(var name: String) {
var age = 26
override fun toString(): String {
return "${this.name} ${this.age}"
}
}
필요하다면 equals와 hashCode를 재정의 하여 모든 프로퍼티를 비교할 수 있도록 해주어야 한다.
override fun equals(other: Any?): Boolean {
if (other == null || other !is User) return false
return this.name == other.name && this.age == other.age
}
override fun hashCode(): Int {
return Objects.hash(name, age)
}
hashCode란 객체를 구별하는 정수값(레퍼런스)을 말한다.
별도의 String변환 과정 없이 바로 문자열의 형태로 출력할 수 있다.
- 기본적으로 주 생성자의 프로퍼티만 변환 된다.
객체의 복사본을 만들어 반환한다.
- 리턴되는 객체는 얕은 복사로 진행된다.
- 인자로 생성자에 정의된 프로퍼티를 넘길 수 이다.
- 이 경우 해당 프로퍼티의 값만 변경되고 나머지 값은 동일하게 생성된다.
얕은 복사
Data Class의 copy는 얕은 복사 이기에 몇가지 문제가 발생할 수 있다.
fun main() {
val user1 = User("이호성", 25, mutableListOf("이터널리턴", "오버워치", "DJMAX"))
val user2 = user1.copy()
user2.age = 26
user2.game.add("마블스냅")
println("user1 : $user1")
println("user2 : $user2")
}
data class User(val name: String, var age: Int, val game: MutableList<String>)
user2는 user1을 copy하여 생성하였고 user2의 age값과 game 값을 변경하였다.
하지만 출력된 결과를 보면 age값은 문제없이 변경되었지만 game의 경우 user1의 값까지 함께 바뀐 것을 확인할 수 있었다.
이를 통해 기본형 타입의 변수들은 깊은 복사를 통해 이루어지지만 그 외의 변수들은 얕은 복사로 이루어 지는 것을 알 수 있다.
copy를 재정의 하여 사용하는 것으로 깊은복사처럼 사용할 수 있다.
data class User(val name: String, var age: Int, val game: MutableList<String>) {
fun copy() = User(name, age, game.toMutableList())
}
구조분해를 통해 변수에 값을 대입할 수 있다.
val user1 = User("이호성", 25,mutableListOf("이터널리턴", "오버워치", "DJMAX"))
val name = user1.name
val age = user1.age
val game = user1.game
일반적으로 클래스 내의 변수를 다른 변수에 대입하기 위해서는 위와 같은 방식을 사용할 수 있다.
하지만 Data Class의 경우 구조분해를 지원하기 때문에 간단하게 표현할 수 있다.
val (name, age, game) = user1
괄호안에 선언된 변수들은 이름에 상관없이 생성자의 프로퍼티들이 순서대로 대입된다.
때문에 순서에 유의하며 사용해야 한다.