자바 플랫폼에서는 클래스가 equals, hashCode, toString등의 메서드를 구현해야 합니다.
코틀린에서는 이런 매서드를 기계적으로 생성하는 작업을 보이지 않는 곳에서 해주게 된다.
따라서 필수 메서드로 인한 더러움(?) 없이 소스코드를 깔끔하게 유지할 수 있다.
자동 메서드를 구현 → 보일러 플레이트 감소!
문자열 표현 : toString()
기본 제공되는 문자열 표현은 Client@aejvn123 같은 방식에서 기본 구현을 바꾸려면 toString 메서드를 오버라이드 해아하며 다음과 같이 구현된다.
class Client(val name : String, val code : Int){
override fun toString(): String {
return "Clent(name = $name, code = $code)"
}
}
객체의 동등성 : equals()
서로 다른 두 객체가 내부에 동일한 데이터를 포함하는 경우 그 둘을 동등한 객체로 간주해야 할 수 있다.
val client1 = Client("밴댕이",1234)
val client2 = Client("밴댕이",1234)
println(client1 == client2)
위의 Client class 를 바탕으로 초기화한 객체의 비교 결과는 false이다.
두 객체의 동등성을 검사하려면 equals를 오버라이드 할 필요가 있다는 의미가 된다.
동등성 동일성 이란?
동등성( ‘==’ )
- 두 객체가 동등함을 비교하며 즉, 값이 같은지 비교
- 동일한 값을 비교 → equals는 동등성 비교
동일성( ‘===’)
- 두 객체가 동일하다는 것을 비교함. 즉, 메모리 상의 같은 위치를 참조하는지 비교
- 메모리 주소 비교 판단
예시
val a: String = "test"
val b: String = "test"
val c: String = a
println(a == b) // true (동등성)
println(a === b) // false (동일성)
println(a === c) // true (동일성)
다음과 같은 결과가 나오게 된다는 것이다.
이제 동등성에 대한 이해와 equals를 추가한 Client 클래스를 살펴보자
class Client(val name : String, val code : Int){
override fun toString(): String {
return "Clent(name = $name, code = $code)"
}
override fun equals(other: Any?): Boolean {
if(other == null || other !is Client)
return false
return name == other.name && code == other.code
}
}
fun main(){
val client1 = Client("밴댕이",1234)
val client2 = Client("밴댕이",1234)
println(client1 == client2) // true 결과 반환
}
프로퍼티의 값이 모두 동일하니 두 객체는 동등하다고 볼 수 있다.
하지만 Client Class가 더욱 복잡한 작업을 수행하다보면 제대로 동작하지 않는 경우가 존재한다.
이와 관련해 hashCode 없다는 점이 원인이다.
hash Container : hasCode()
equals를 오버라이드 할 때 반드시 hashCode를 오버라이드 해야하며 이유를 살펴보자
fun main(){
val hashSet = hashSetOf(Client("밴댕이",1234))
println(hashSet.contains(Client("밴댕이",1234)))
}
다음의 결과를 true라고 예상할 수 있지만 실제로는 false가 나오게 된다.
이 이유는 hashCode메서드를 정의하지 않았기 때문이다.
왜?
JVM언어에서는 equals()가 true를 반환하는 두 객체는 반드시 같은 hashCode()를 반환해야 하는 제약이 있는데 Client는 이를 어기고 hashCode 메서드가 존재하지 않는다.
HashSet같읜 경우 객체의 해시코드를 비교하고 해시코드가 같은 경우에만 실제로 값을 비교한다
(해시코드 → 실제 값 비교 순서)
따라서 다음과 같이 세가지를 오버라이드 해야한다.
class Client(val name : String, val code : Int){
override fun toString(): String {
return "Clent(name = $name, code = $code)"
}
override fun equals(other: Any?): Boolean {
if(other == null || other !is Client)
return false
return name == other.name && code == other.code
}
override fun hashCode(): Int {
return name.hashCode() * 31 + code
}
}
데이터 클래스 Data Class : 클래스가 정의해야하는 메서드 자동 생성
앞서 맨 위에서 말했듯이 DataClass는 기계적으로 생성을 해준다는 것을 말을 했다.
data라는 변경자를 앞에 붙이면 필요한 메서드를 컴파일러가 자동으로 만들어준다!
위의 class 를 아래와 같이 data 변경자를 통해 쉽게 정의할 수 있다.
data class Client(val name : String, val code : Int)
주의할 점은 여기서 equals와 hashCode는 주 생성자에 나열된 모든 프로퍼티만을 고려해 만들어지며 주 생성자 밖에서 정의한 프로퍼티는 고려의 대상이 아니다. (당연한 말이긴 함)
데이터 클래스와 불변성 : copy()
데이터 클래스의 모든 프로퍼티를 읽기 전용으로 만들어서 데이터 클래스를 불변 클래스로 만들어야 한다는 얘기를 많이 들었을 것이다.
이를 바탕으로 데이터 클래스 인스턴스를 불변 객체로 더 쉽게 활용할 수 있게 코틀린 컴파일러는 한가지 메서드를 제공해준다.
객체를 복사하면서 일부 프로퍼티를 바꿀수 있는 Copy 메서드 이다.
class Client(val name : String, val code : Int){
//세가지 메서드 toString, equals, hashCode
fun copy(name : String = this.name, code : Int = this.code) : Client{
return Client(name, code)
}
}
fun main(){
val client1 = Client("밴댕이",1234)
println(client1.copy(code = 4321))
}
//Clent(name = 밴댕이, code = 4321)
data class를 통해 이렇게 네가지 메서드를 관찰해보았다.
data class 정리 (Data Class vs class)
: 데이터 저장과 전달을 위해 사용되며 객체의 상태와 동작을 표현하는 클래스와 성격이 다르다.
- 네가지 메서드를 자동으로 구현해 주어 보일러 플레이트를 줄여줌
- 생성자는 한개 이상의 프로퍼티를 가져야 함
- 불변 객체로 사용되며 속성 값 변경을 할 수 없게
'Android & Kotlin > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 object/Companion Object(동반 객체)/Anonymous Object(무명 객체) (0) | 2024.05.08 |
---|---|
[Kotlin] 클래스 위임 : by / 일급 객체 / 데코레이터, 위임 패턴 (0) | 2024.05.07 |
[Kotlin] 코틀린 Sealed class ,Interface (0) | 2024.05.03 |
[Kotlin] Inner, nested class 내부 중첩 클래스(+ Recyclerview 리사이블러뷰) (0) | 2024.05.03 |
[Kotlin 상속제어 변경자] open, final, abstract, override (1) | 2024.05.02 |
댓글