언어/Kotlin
[EffectiveKotlin] equals, hashcode 규약과 data class, jpa
원석💎-dev
2022. 6. 1. 23:50
반응형
먼저 jdk의 equals, hashcode 함수를 확인해보자. 모든 동등성을 주소값으로 처리한다.
class Object {
.
.
@IntrinsicCandidate
public native int hashCode();
.
.
public boolean equals(Object obj) {
return (this == obj);
}
.
.
}
- equals는 객체의 주소값으로 같은지 다른지 판단하고 있다.
- Kotlin으로 치면 ===
- hashCode는 메모리에서 객체의 Hash 주소값을 반환한다.
- native 키워드는 함수를 java에서 직접 구현하지 않고, 외부의 함수를 호출할 수 있다.
- 개발 규약
- hashCode 규약
- 어떤 객체의 hashCode는 여러 번 호출해도 항상 같은 결과가 나와야 한다.
- equals의 결과로 두 객체가 같다고 나오면 hashCode의 결과도 같야 한다.
- equals 규약
- 반사적 : x가 Null이 아닌 값이라면 x.equals(x)는 true이다.
- 대칭적 : x와 y가 null이 아닌 값이라면, x.equals(y)는 y.equals(x)와 같다.
- 연속적 : x, y, z가 null이 아닌 값이고 x.equals(y)와 y.equals(z)가 ture라면, x.equals(z)도 true이다.
- 일관적 : x와 y가 null이 아니라면 x.equals(y)는 여러 번 실행하더라도 항상 같은 결과를 낸다.
- 널 관련된 동작 : x가 null이 아닌 값이라면, x.equals(null)은 항상 false를 리턴해야 한다.
- hashCode를 override하면 equals도 override 한다.
- 왜 override 하나?
- Kotlin의 문법과 객체의 사용법을 일관성있게 사용하기 위함이다.
- List의 contains함수는 equals를 이용하여 동일한 객체를 찾는다. 그러므로 아래와 같은 코드는 개발자의 의도와는 다르게 동작한다.
class Test(
val first: String,
val second: String,
val third: String,
)
val testList = listOf(Test("a", "b", "c"), Test("d", "e", "f"))
// Test("a", "b", "c")가 있는지 찾고싶다.
testList.contains(Test("a", "b", "c")) // false
- hashCode는 HashMap에서 사용되는 HashTable에서 사용된다. 같은 값을 return할 경우 성능에 영향을 미친다.
- 사용
1. String의 equals와 hashCode : 사용자 정의 equals와 hashCode
class String {
.
.
// StringUTF16의hashCode
public static int hashCode(byte[] value) {
int h = 0;
int length = value.length >> 1;
for (int i = 0; i < length; i++) {
h = 31 * h + getChar(value, i);
}
return h;
}
.
.
// StringLatin1의 equals
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
.
.
}
- 우리가 자주 사용하는 String 클래스는 hashCode와 equals를 override하여 사용하기 때문에 아래와 같은 equals(==)사용이 가능하다.
val text1 = "a"
val text2 = "a"
val text3 = String("a".toByteArray())
println(text1 == text2) // true
println(text1 == text3) // true
2. 먼저 data class를 고려하자.
- data class는 컴파일 시점에 equals(), hashCode(), copy(), toString() 함수를 자동으로 생성한다.
- data class의 equals는 생성자에 정의된 모든 값이 동일한지 아닌지를 체크하기 때문에 일부 값만 비교하고 싶은 경우 equals를 새로 override해야 한다.
3. JPA Entity 객체
- data class는 사용하지 말자.
- PK를 활용하여 equals를 구현하자.
- PK가 null인 객체(영속성 컨텍스트에 의해 관리되지 않는 Entity)는 어떻게 equals를 수행할 것인가?
- 정답은 없다. 상황에 맞게 구현하자.
- 결론
- override 전 date class를 먼저 고려하자.
- hashCode override 시 Obejects.hash함수를 먼저 고려하자.
- 규약을 지키면서, equals와 hashCode를 override하자
참고 :
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Object.java
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/String.java
반응형