-
[Java] 이펙티브 자바 (2) - 모든 객체의 공통 메서드Java 2020. 4. 18. 01:13반응형
아이템 10. equals는 일반 규약을 지켜 재정의하라
equals를 재정의하지 않아야 할 때(하나라도 해당한다면)
- 각 인스턴스가 본질적으로 고유
- 인스턴스의 '논리적 동치성(logical equality)'을 검사할 일이 없음
- 상위 클래스에서 재정의한 equals가 하위 클래스도 딱 맞는 경우의 하위 클래스
- 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없음
실수로라도 equals가 호출되는 것을 막고 싶다면?
equals를 override하되, AssertionError를 던지도록 구현해두기
equals를 재정의해야 할 때
- 객체 식별성(object identity)이 아닌 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의되지 않았을 때
- 주로 값 클래스들이 위에 해당
- 값 클래스라해도, 인스턴스 통제 클래스라면 equals를 재정의하지 않아도 됨 - Enum도 여기에 해당
equals 메서드를 재정의할 때의 지켜야 할 일반 규약
equals 메서드는 동치관계(equivalence relation)를 구현하며, 다음을 만족시켜야 함 - 어길 경우 그 객체를 사용하는 다른 객체들이 어떻게 반응하는지 알 수 없음
- 반사성(reflexivity) : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true
- 대칭성(symmetry) : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true
- 추이성(transitivity) : null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y)가 true이고 y.equals(z)도 true이면, x.equals(z)도 true
- 일관성(consistency) : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환
- null-아님 : null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false
양질의 equals 메서드 구현 방법 단계
1. == 연산자로 입력이 자기 자신의 참조인지 확인
2. instanceof 연산자로 입력이 올바른 타입인지 확인
3. 입력을 올바른 타입으로 형변환
4. 입력 객체와 자신의 대응되는 '핵심' 필드들이 모두 일치하는지 하나씩 검사
참고사항
- equals를 재정의할 경우 hashCode도 반드시 재정의
- 일반적으로 별칭(alias)은 비교하지 않는 것이 좋음
- Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말 것
용어
Logical Equality(논리적 동치성) : p ≡ q. 두 (합성)명제 p와 q의 진릿값이 서로 같은 경우
Object Identity(객체 식별성) : 두 객체가 물리적으로 같은가
Equivalence relation(동치관계) : 논리적 동치와 비슷한 성질들을 만족시키는 이항 관계
아이템 11. equals를 재정의하려거든 hashCode도 재정의하라
equals를 재정의한 클래스 모두에서 hashCode도 재정의 해야 함
- equals 비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 함. 단, 애플리케이션을 재 실행하는 경우, 이 값이 달라져도 상관 없음
- equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 같은 값을 반환해야 함
- equals(Obejct)가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없음. 하지만, 다른 객체의 경우 다른 값을 반환해야 해시테이블의 성능이 좋아짐
hashCode 재정의를 잘못한다면?
HashCode를 이용한 API들이 의도와 다르게 동작할 것(예 - HashMap)
아이템 12. toString을 항상 재정의하라
toString의 규약
"모든 하위 클래스에서 해당 메서드를 재정의하라"
재정의한 toString
사용하기 좋고, 디버깅하기 쉬움
어떻게 재정의를 해야할까
그 객체가 가진 주요 정보 모두를 반환하는 것이 좋음
포맷을 명시하든 아니든 의도를 명확히 밝혀야 함(주석)
기능의 경우 toString의 내용을 이용하지 말고, 필요한 값을 얻어올 수 있는 API(메서드)를 따로 제공할 것!
아이템 13. clone 재정의는 주의해서 진행하라
Cloneable Interface
복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스(mixin interface)
구현시 Object의 clone 메서드를 사용할 수 있게 됨 - 필드를 하나하나 복사한 객체 반환
미구현시 CloneNotSupportedException 발생
clone 메서드
사실상 생성자와 동등한 효과
원본 객체에 아무런 해를 끼치지 않는 동시에, 복제된 객체의 불변식을 보장해야 함
참조형 변수의 경우, 깊은 복사를 해주어야 함! (꼭)
재정의한 메서드는 throws절을 없애야 함 - 검사 예외를 던지지 않아야 사용하기 편하기 때문
쓰레드 안전 클래스를 작성할 때는, 적절히 동기화도 해야 함
상속용 클래스는 Cloneable을 구현하면 안됨
- 구현해두고 하위 클래스에서 Cloneable 구현 여부를 선택하게 하던지(Object.clone()처럼)
- clone을 퇴화시켜 두던지(final 메서드로 CloneNotSupportedException을 던지게끔)
권장
복제 기능은 생성자와 팩터리를 이용할 것 - (예 - new ArrayList<>(복사할List))
단, 예외적으로 배열만은 clone 메서드 방식이 가장 깔끔함
final 클래스라면 Cloneable을 구현해도 위험이 크지 않지만, 성능 최적화 관점에서 검토한 후 별다른 문제가 없을 경우에만 드물게 허용할 것
아이템 14. Comparable을 구현할지 고려하라
Comparable 인터페이스의 유일 메서드 - compareTo
단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭함
CompareTo 메서드의 일반 규약
이 객체와 주어진 객체의 순서를 비교. 이 객체가 주어진 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환. 비교할 수 없는 타입의 경우 ClassCastException을 던짐
다음 설명에서 sgn(표현식) 표기는 수학에서 말하는 부호 함수(signum function)을 뜻하며, 표현식의 값이 음수, 0, 양수일 때 -1, 0, 1을 반환하도록 정의함
- Comparable을 구현한 클래스는 모든 x, y에 대해 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))여야 함. (x.compareTo(y)가 예외를 던진다면 y.compareTo(x)도 예외를 던져야 함)
- Comparable을 구현한 클래스는 추이성을 보장해야 함. 즉, (x.compareTo(y) > 0 && y.compareTo(z) > 0)이라면, x.compareTo(z) > 0임
- Comparable을 구현한 클래스는 모든 z에 대해 x.compareTo(y) == 0이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z))임
- 필수가 아닌 권장이지만, 꼭 지키는 것이 좋음. (x.compareTo(y) == 0) == (x.equals(y))여야 함. 지키지 않는 모든 클래스는 해당 사실을 명시해야 함(주의 : 이 클래스의 순서는 equals 메서드와 일관되지 않음)
구현시 유의할 점 - 정수 비교
- <, >를 사용하는 방식은 거추장스럽고 오류를 유발하므로 박싱된 기본 타입 클래스의 정적 메서드인 compare를 이용하여 비교할 것
- 값의 차(-)를 기준으로 반환하는 방식은 사용하지 말 것 : 정수 오버플로를 일으키거나, 부동소수점 계산 방식에 따른 오류를 낼 수 있음
반응형'Java' 카테고리의 다른 글
[Java] 이펙티브 자바 (4) - 제네릭 (0) 2020.04.18 [Java] 이펙티브 자바 (3) - 클래스와 인터페이스 (0) 2020.04.18 [Java] 이펙티브 자바 (1) - 객체 생성과 파괴 (0) 2020.04.18 [Java] GOF 디자인패턴 용어 정리 (0) 2020.04.16 자바의 I/O (0) 2020.03.13