본 게시글은
data class (tistory.com)
을 토대로 질문이 들어와서 추가 정리 내용을 작성합니다.
Kotlin 의 data class 는, 데이터를 보유하기 위하여 만든 클래스입니다.
만약 다음과 같은 조건으로 User 라는 Class 를 만들어야 하는 경우를 생각해 보겠습니다.
1. 이름을 의미하는 문자열 Type 의 name filed 를 포함한다.
2. 나이를 의미하는 정수 Type 의 age filed 를 포함한다.
3. 생성하는 순간에 name 과 age를 parameter 로 전달받고, 전달받은 name 과 age 로 초기화한다.
4. 한번 생성된 User라는 객체의 name 과 age filed 는 read only 이다.
5. 출력을 위하여 toString() 을 호출하는 순간 User{name='value', age=value} 형태로 출력한다.
6. 값을 비교하기 위하여 eqauls() 를 구현한다.
7. 객체의 값이 일치하다면, 같은 주소값을 반환하는 hashCode() 를 구현한다.
<Java 로 구현하기>
public class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age &&
Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
<Kotlin 으로 구현하기>
data class User(
val name: String,
val age: Int
)
1. toString()
Java 에서는 기본적으로 객체의 toString() 은
이름@[16진수로 표시한 hashcode] 를 반환합니다.
만약, 위에서 작성한 User 라는 Java class 에서 hashCode() 를 아래와 같이 수정한다면,
출력결과는 User@0 이 됩니다.
따라서 Java 에서는 toString() 을 Override 하여 원하는 출력결과를 명시해야합니다.
(IDE 가 좋아졌기 때문에, 자동완성 기능으로 조건에서 언급한 기준처럼 작성은 되지만, 반드시 Override 해야하는 것은 변하지 않습니다.)
@Override
public int hashCode() {
return 0;
}
그러나 Kotlin 의 data class 은 이를 사용자가 직접 작성하지 않더라도, 조건에서 언급한 기준으로 동작합니다.
만약 Kotlin 의 data class 에서 toString() 을 아래와 같이 Override 하는 순간,
Java 의 객체 toString() 과 동일하게 동작하여
이름@[16진수로 표시한 hashcode] 를 반환합니다.
override fun toString(): String {
return super.toString()
}
2. eqauls()
Objecet 의 equals() 는 주소값 비교입니다.
즉, 만들어진 객체가 완전히 같은 주소값을 가리키고 있으면 true, 아니면 false 를 반환합니다.
흔히들 Java 의 equals 는 값비교, == 은 주소비교 라고 하지만, 재정의된 Class 에 한에서만 위와 같이 동작합니다.
* String 에서 equals 는 이미 재정의되어 있기 때문에 값비교가 성립하는 것입니다. 모든 Class 가 성립하지 않습니다.
Kotlin data class 는 이러한 equals 가 이미 재정의되어 값비교를 할 수 있게 되어 있습니다.
즉, Kotlin 의 data class 에서 아래와 같이 사용자가 직접 Override 하는 순간,
그 조건이 Object 의 주소비교가 됩니다.
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
3. hashcode()
객체를 식별할 수 있는 값을 반환합니다. (주소값은 유니크해서 일반적으로 주소값을 반환하지만, 주소값이 아닐 수 있습니다. 중요한건 해당 값은 "유니크" 해야 한다는 점입니다.)
"유니크" 하다는 것은 Key <-> Value 로 치면, Key 로 접근했을 때 항상 동일한 Value 에 접근할 수 있어야 한다는 것입니다. 그러나, 주소값만 반환한다면, 논리적으로 동일한 값을 가졌음에도 불구하고 새로 생성하는 객체마다 늘 다른 값을 반환합니다.
이러한 이유로 Java 의 class 는 hashcode 를 Override 하여 논리적으로 같을 경우 같은 hashcode를 반환하도록 작성하고,
Kotlin 의 data class 는 해당경우에 같은 hashcode 를 반환하도록 되어 있습니다.
Java Class | Kotlin data Calss | |
toString() | Override 하지 않으면 이름@16진수 hashcode 반환 Override 할 때 super.toString() 만 작성하면 이름@16진수 hashcode 반환 |
Override 하지 않으면 자동생성되는 String 을 반환 Override 할 때 super.toString() 만 작성하면 이름@16진수 hashcode 반환 |
equals() | Overrdie 하지 않으면 객체가 주소값이 같은지만 판단 Overrride 할 때 super.equals(other) 만 작성하면 역시 객체가 완전히 같은지만 판단 |
Override하지 않으면 객체의 값이 같은지를 판단 Override 할 때 super.equals(other) 만 작성하면 역시 객체가 완전히 같은지만 판단 |
hashcode() | Override 하지 않으면 서로 다른 객체의 value 가 같더라도, 다른 유니크값을 반환합니다. Override 할 때 super.hashCode() 를 작성하면 서로 다른 객체의 value 가 같더라도, 다른 유니크값을 반환합니다. Override 할 때 value 를 포함하여 작성하면 서로 다른 객체의 value 가 같으면, 같은 유니크값을 반환합니다. |
Override 하지 않으면 value 를 포함하도록 작성되어있어서 서로 다른 객체의 value 가 같으면, 같은 유니크값을 반환합니다. Override 할 때 super.hashCode() 를 작성하면 서로 다른 객체의 value 가 같더라도, 다른 유니크값을 반환합니다. Override 할 때 value 를 포함하여 작성하면 서로 다른 객체의 value 가 같으면, 같은 유니크값을 반환합니다. |
* 값이 같은지?? 논리적으로 같은지??
어짜피 equals 가 true 이면 그 값이 동일하다는 건데, hashcode 도 true 아닌가요???????
User 라는 class 와 그 내용이 완전히 같은 User2 라는 클래스가 있다고 가정하겠습니다.
아래처럼 eqauls() 는 값 뿐만 아니라 class 도 확인합니다.
즉 생성할때 object 값이 같더라도, class 가 다르면 서로 다르다고 판단합니다.
그러나, hashCode() 는 논리적으로 같음을 보기 때문에, 생성하는 class 가 다르더라도,
object 값이 같다면, 서로 같은 유니크값을 반환하게 됩니다.
val a = User("DevHyeon", 28)
val b = User("DevHyeon", 28)
val c = User2("DevHyeon", 28)
//true
println(
a == b
)
//true
println(
a.hashCode() == b.hashCode()
)
//false
println(
a == c
)
//true
println(
a.hashCode() == c.hashCode()
)
너무나도 헷갈리고, 평소에 자주 사용하지 않는 한 나중에 다시보면 아! 이랬지! 싶은 내용입니다만,
헐? 그런거야? 보다는 아! 이랬지!
(적어도, 분명 두개가 다른건 확실한데.. hashCode() 가 반환하는게 무조껀 주소값이 아니였는데... 정도까지는 알고있는게 좋아보입니다.)