본 게시글은
data class (tistory.com)

 

data class

pojo : 메소드가 작동을 하는 것이 아니라 비어 있는 틀 역할을 하는 클래스 data class 클래스이름 (변수,변수....) 변수는 class의 property처럼 사용. dataclass 도 init과 메소드 생성 가능 컴파일러가

wldnjs1277.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() 가 반환하는게 무조껀 주소값이 아니였는데... 정도까지는 알고있는게 좋아보입니다.)

+ Recent posts