그냥 두면 클래스의 인스턴스는 오직 자기 자신과만 같게 된다
다음과 같은 상황이면 equal는 재정의 하지 않는것이 좋다
객체 식별성이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의 되지 않았을 때 재정의
equals를 재정의 해야할때 지켜야 하는 규약
동치관계: 집합을 서로 같은 원소들로 이루어진 부분집합으로 나누는 연산
equals 메서드가 쓸모 있으려면 모든 원소가 같은 동치류에 속한 어떤 원소와도 서로 교환할 수 있어야 한다
구체 클래스를 확장해 새로운 값을 추가하면서 equals 규약을 만족시킬 방법은 존재하지 않는다
→ 상속 대신 컴포지션을 사용하면 해당 문제를 우회할 수 있음
equals 메서드 구현 방법
equals 성능 향상법
equals 구현 시 주의 사항
전형적인 hashhCode 메서드
@Override public int hashCode(){
int result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
return result;
}
// Guava 라이브러리 사용 시 더 간단하나 성능은 조금 아쉬워진다
@Override public int hashCode(){
return Objects.hash(lineNum, prefix, areaCode);
}
성능을 높이겠다고 해시코드를 계산할 때 핵심 필드를 생략해서는 안된다
hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세하게 공표하지 않아서 클라이언트가 이 값에 의존하지 않고 추후에 계산 방식을 바꿀 수 있게 해준다
toString은 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야 한다
모든 하위 클래스에서 toString을 재정의 해야 된다
toString을 잘 구현한 클래스는 사용하고 디버깅하기 쉬움
toString을 구현할 때 반환값의 포맷을 문서화할지 정해야 한다
포맷의 장점: 명확하고 읽기 편함
포맷의 단점: 해당 포맷에 얽매인다
포맷 명시 여부와 상관 없이 toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하는게 좋다
Cloneable을 구현한 클래스의 인스턴스에서 clone을 호출하면 그 객체의 필드들을 하나하나 복사한 객체를 반환, 그렇지 않은 클래스의 인스턴스에서 호출하면 CloneNotSupportedException을 던진다
clone 메서드는 사실상 생성자와 같은 효과를 낸다.
clone은 원본 객체에 아무런 해를 끼치지 않는 동시에 복제된 객체의 불변식을 보장해야 한다
상속해서 쓰기 위한 클래스는 Cloneable을 구현해서는 안 된다
//하위 클래스에서 cloneable을 지원하지 못하게 하는 clone 메서드
@Override
protected final Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException();
}
Cloneable을 구현한 스레드 안전 클래스를 작성할 때는 clone 메서드 역시 적절히 동기화 해줘야 한다
final 클래스라면 Cloneable을 구현해도 위험이 크지 않지만 성능 최적화 관점에서 검토한 후 별다른 문제가 없을 때만 드물게 허용해야 한다
Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서가 있음을 뜻한다
compareTo 규약
sgn function: 이 객체와 주어진 객체의 순서를 비교, 이 객체가 주어진 객체보다 적으면 음의 정수, 같으면 0, 크면 양의 정수를 반환. 객체를 비교할 수 없으면 ClassCastException을 던짐
Comparable을 구현한 클래스는 다음과 같아야 한다
compareTo 메서드에서 관계 연산자 <와>를 사용하는 이전 방식은 오류를 유발하니 사용하지 않는다
클래스에 핵심 필드가 여러개라면 가장 핵심 필드부터 비교해 나간다