-
아이템10. equals는 일반 규약을 지켜 재정의하라책/이펙티브자바 2021. 7. 2. 19:00
아래에 상황 중 하나에 해당한다면 equals 는 재정의 하지 않는 것이 최선이다.
- 각 인스턴스가 본질적으로 고유하다.
- 값을 표현하는게 아니라 동작하는 개체를 표현하는 클래스
- ex) Thread
- 인스턴스의 '논리적 동치성'을 검사할 일이 없다.
- 상위 클래스에서 재정의한 equals 가 하위 클래스에도 딱 들어맞는다.
- 예를들어 대부분의 Set, List 구현체들은 AbstractSet, AbstractList가 구현한 equals 를 그대로 사용한다.
- 클래스가 private 이거나 package-private이고 equals 메소드를 호출할 일이 없다.
// euqals가 실수로라도 호출되는걸 막고싶을 때 @override public boolean equals(Obejct o) { throw new AssertionError(); }
그렇다면 언제 equals() 를 재정의 해야 하는가 ?
객체 식별성(object identity: 두 객체가 물리적으로 같은가) 이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의 되지 않았을 때이다.
주로 Integer, String 과 같은 값 클래스들이 이에 해당한다.
equals() 를 재정의할 때 반드시 따라야하는 일반 규약
- 반사성 (reflexivity)
- null이 아닌 모든 참조 값 x에 대해, x.eqauls(x) = true
- 대칭성 (symmetry)
- null이 아닌 모든 참조 값 x, y에 대해, x.eqauls(y) 가 true 이면 y.equals(x) 도 true 이다.
- 추이성 (transitivity)
- null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y) 가 true이고 y.eqauls(z) 도 true이면 x.equals(z) 도 true 이다.
- 일관성 (consisteny)
- null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)를 반복해서 호출하면 항상 같은 결과를 return 한다.
- null-아님
- null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false이다.
양질의 equals() 를 구현하는 방법
- == 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
- instanceof 연산자로 입력이 올바른 타입인지 확인한다.
- 입력을 올바른 타입으로 형변환한다.
- 입력 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치하는지 하나씩 검사한다.
주의사항
- 참조 타입 필드는 각각의 equals 로 비교
- float과 double을 제외한 기본 타입 필드는 == 연산자로 비교
- float과 double은 Float.compare, Double.compare 로 비교한다.
- 배열의 모든 원소가 핵심 필드라면 Arrays.equals 메서드 들 중 하나를 사용하자.
- null 비교는 Objects.equals() 로 비교해 NullPointerException 을 예방하자.
- 최상의 성능을 바란다면 다를 가능성이 더 크거나 비교하는 비용이 싼 필드를 먼저 비교하자.
- equals 재정의 할땐 hashCode도 반드시 재정의하자.
- 너무 복잡하게 해결하려 들지 말자.
- 매개변수로 Object 외의 타입을 받지말자.
equals()를 구현했다면 다음 사항을 확인해보자.
- 확인만 하지 말고 단위테스트를 작성해보자.
- 세 요건 중 하나라도 실패한다면 원인을 찾아서 수정하자.
- 대칭적인가 ?
- 추이성이 있는가 ?
- 일관적인가 ?
전형적인 equals() 의 예
@Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof PhoneNumber)) { return false; } PhoneNumber pn = (PhoneNumber)o; return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode; }
결론
- 꼭 필요한 경우가 아니면 equals() 재정의는 하지말자.
- 대부분의 경우에서는 Obejct.equals()가 원하는 비교를 수행해준다.
- 그럼에도 재정의를 해야한다면 해당 클래스의 핵심 필드 모두를 빠짐없이, 다섯가지 규약을 확실히 지켜가며 비교한다.
'책 > 이펙티브자바' 카테고리의 다른 글
아이템12. toString을 항상 재정의하라 (0) 2021.07.19 아이템11. equals를 재정의하려거든 hashCode도 재정의하라 (0) 2021.07.02 아이템9. try-finally보다는 try-with-resources를 사용하라 (0) 2021.06.26 아이템8. finalizer와 cleaner 사용을 피하라 (0) 2021.06.26 아이템7. 다 쓴 객체 참조를 해제하라 (0) 2021.06.23 - 각 인스턴스가 본질적으로 고유하다.