Java의 동등성에 대한 고찰
· 21 min read
개론
우연히 취준방에서 이런 질문을 받았다.
옛날에 이 방에서 a.equals(b)말고도 Objects.equals(a, b) 쓴다는 내용을 봤던 적이 있었는데 Objects.equals를 쓰는 이유가 단순히 null에 대해 안전하기 때문에 사용하는 걸까요?? 가독성은 전자가 더 좋다고 느껴집니다.
단순히 Null Safety를 위해서 저렇게 사용한다! 라고 이야기 할 수도 있지만, 사실 동등성을 비교하는데 있어서 고려해야 할 것은 생각보다 많다. (단순히 NPE를 방지하는 것이 답이 아닌 경우도 있고...)
원래는 질문에 답을 하기 위해 간단하게 작성해보려 했으나, 작성하다보니 자연스럽게 equals() 를 포함한 동등성 자체에 대한 고민을 주욱 작성하게 되었다.
기본적인 equals() 설계 방식
일단은 equals()
를 좀 살펴보자. Java의 equals()
에 대한 명세에는 다음과 같은 내용이 포함된다.
Indicates whether some other object is "equal to" this one. The equals method implements an equivalence relation on non-null object references:
- It is reflexive: for any non-null reference value x, x. equals(x) should return true.
- It is symmetric: for any non-null reference values x and y, x. equals(y) should return true if and only if y. equals(x) returns true.
- It is transitive: for any non-null reference values x, y, and z, if x. equals(y) returns true and y. equals(z) returns true, then x. equals(z) should return true.
- It is consistent: for any non-null reference values x and y, multiple invocations of x. equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
- For any non-null reference value x, x. equals(null) should return false.
한글로 간단하게 번역하면 다음과 같다.
- 반사성 (Reflexive): null이 아닌 참조값 x에 대해,
x.equals(x)
는 참이다. - 대칭성 (Symmetric): null이 아닌 참조값 x, y에 대해,
x.equals(y)
가 true 이면y.equals(x)
도 참이다. - 추이성 (Transitivity): null이 아닌 참조값 x, y, z에 대해,
x.equals(y)
가 참이고y.equals(z)
가 참이면x.equals(z)
도 참이다. - 일관성 (Consistency): null이 아닌 참조값 x, y에 대해,
x.equals(y)
의 값은 언제나 참이거나 거짓이다. (즉, 결과가 변하지 않는다.) - Not Null: null이 아닌 참조값 x에 대해,
x.equals(null)
은 거짓이다.
생각보다 단순하다고 느껴질 수 있는 원칙들이지만 막상 구현하려고 하다보면 생각보다 원칙이 깨지기가 정말 쉽다. (Effective Java의 Item 10만 참고해도 깨질 수 있는 사례가 정말 많이 언급된다.)
물론 우리가 이 글에서 다루고자 하는 것은