[item-10] 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이다.
[item-11] equals를 재정의하려거든 hashCode도 재정의하라
hashCode는 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 쓰이는 메서드
- 특정한 객체를 해쉬코드(일정한 길이의 문자열로 표시)
Object의 명세에는 hashCode에 관련한 규약이 정해져 있습니다
오류 발생 상황
Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
m.get(new PhoneNumber(707, 867, 5309)) // "Jenny"가 아닌 null이 나온다.
좋은 해시코드 작성요령
// One-line hashCode method - mediocre performance (성능이 민감하지 않은 경우)
@Override public int hashCode() {
return Objects.hash(lineNum, prefix, areaCode);
}
// hashCode method with lazily initialized cached hash code
private int hashCode; // Automatically initialized to 0
@Override public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
해시코드 생성시 주의사항
- 성능을 높이기 위해 해시코드를 계산할 때 핵심 필드를 생략해서는 안됩니다. 해시코드의 분포를 고르게 나누어주는 효과가 있기 때문입니다.
- hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세히 공표하지 않는 것이 좋습니다. 그래야 클라이언트가 이 값에 의지하지 않고, 추후에 계산방식을 바꿀 수 있습니다.
[item-12] 항상 toString을 재정의하라
- Object에서 정의한 toString()을 재정의하고 사용하지 않으면, 클래스 이름@16진수로 표시한 해시코드 포맷으로 반환
- 디버깅용으로 많이 사용
[item-13] Clone 재정의는 주의해서 진행하라
- 복제해도 되는 클래스임을 명시하는 용도의 인터페이스
- Clonable 자체에는 메서드가 없고, Cloneable을 구현하고 Object의 clone() 메서드를 호출하면 값을 복사한 객체를, 그렇지 않으면 CloneNotSupportedException을 반환
메서드 재정의 방법
- 접근 제한자는 public으로 함
- 반환 타입은 클래스 자신으로 변경
- 가장 먼저 super.clone() 호출
- 필요한 필드는 모두 수정
Example
@Override
public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // 일어날 수 없는 일이다.
}
}
- 재정의한 메서드의 반환 타입은 상위 클래스의 메서드가 반환하는 타입의 하위 타입
- 클라이언트가 형변환하지 않아도 되게끔, super.clone()에서 얻을 객체를 반환하기 전에 형변환
- try-catch로 감싼 이유는 Object의 clone 메서드가 검사 CloneNotSupportedException을 던지도록 선언되었기 때문입니다. → Clonable을 구현하는데 예외를 던질 필요가 X
[item-14] Comparable을 구현할지 고려하라
Comparable 인터페이스의 유일한 메서드이다. equals처럼 동작하지만, 추가적으로 순서까지 비교할 수 있다. (반환 값이 1이면 더 큰 값, 0이면 동일한 값, -1이면 작은 값을 의미한다) Comparable을 구현했다는 것은 그 클래스의 인스턴스들 간에 자연적인 순서가 있음을 뜻합니다
CompareTo의 규약
1. 반사성 : 두 객체 참조의 순서를 바꿔 비교해도 예상한 결과가 나와야 .
2. 추이성 : 첫 번째보다 두 번째 객체가 크고, 두 번째 객체가 세 번째 객체보다 크면, 세 번째 객체는 첫 번째 객체보다 커야 한다.
3. 대칭성 : 크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다.
'Java > Effective Java' 카테고리의 다른 글
[Effective Java] 4장. 클래스와 인터페이스 (0) | 2024.08.08 |
---|---|
[Effective Java] 2장. 객체 생성과 파괴 (0) | 2024.08.08 |