조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.
1. equals()는 일반 규악을 지켜 재정의하라
- 정의하지 않으면 참조 주소값만 가지고 동치여부를 따짐
재정의할 필요 없는 경우
싱글턴 인스턴스일 경우
상위 클래스에서 재정의한 equals()가 하위 클래스에도 동일
- AbstractSet - Set
- AbstractList - List
재정의할 필요 있는 경우
상위 클래스가 equals()를 정의하지 않았을 경우
- 값 클래스
- Map의 key
- Set의 원소
2. hashCode()도 재정의해라
- equals()만 재정의할 경우, hash값으로 동치성을 판별하는 컬렉션 사용 시 결과가 이상하게 나옴
hashCode() 규약
- hashCode()값을 생성할 때, 반드시 핵심 필드가 포함되어야 함
- hashCode()를 여러번 호출해도 일관된 값을 리턴해야 함
- equals()가 같다 판단했을 경우, hashCode()값도 같아야 함
- equals()가 다르다 판단했을 경우, hashCode()값이 같을 수도 있음
3. toString()는 항상 재정의하라
- 간결하면서 유익한 정보를 반환해야 함
기본값
- 클래스_이름@해시코드(16진수)
4. clone()는 주의해서 재정의하라
Cloneable
clone()
- Object의 protected 메서드
- 필드들을 하나하나 복사한 객체를 반환
- 원본 객체에 아무런 해를 끼치지 않는 동시에 복제된 객체의 불변을 보장해야 함
- 동기화 필요
Stack
public class Stack implements Cloneable {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
배열 복제 시, clone()이 권장됨
- 타입 안정성 지원
- 런타임 타입과 컴파일 타임 타입 모두가 원본 배열과 똑같은 배열을 반환
- System.arraycopy()는 원본 배열의 타입을 보장하지 않음
- 선언된 타입으로 복사됨
elements 필드는 final로 선언되면 안됨
- 값을 할당할 수 없으므로 복제 불가
HashTable
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// 이 Entry 객체가 첫 원소인 연결 리스트를 순환문으로 복사
Entry deepCopy() {
Entry result = new Entry(key, value, next);
for (Entry p = result; p.next != null; p = p.next) {
p.next = new Entry(p.next.key, p.next.value, p.next.next);
}
return result;
}
}
@Override
public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] != null) {
result.buckets[i] = buckets[i].deepCopy();
}
}
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
hashtable은 bucket의 배열
- bucket을 clone할 경우, 원본과 같은 연결리스트를 참조함
- 원본과 복제본 모두 예기치 않게 동작할 수 있음
- 각 버킷을 구성하는 연결 리스트를 복사해야 함
- HashTable의 Entry는 깊은 복사를 정의함
5. Comparable을 구현할 지 고려하라
compareTo()
static Comparator<Object> hashCodeOrder = new Comparator<>() {
public int compare(Object o1, Object o2) {
return Integer.compare(o1.hashCode(), o2.hashCode());
}
}
- 배열 혹은 리스트에서 원소들을 정렬할 때 기준을 정의하는 메서드
- 값이 작을 수록 먼저 나옴
'Java' 카테고리의 다른 글
[Effective Java] 4-2. 클래스와 인터페이스: 상속보다는 컴포지션을 사용하라 (2) | 2024.12.27 |
---|---|
[Effective Java] 4-1. 클래스와 인터페이스: 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2024.12.27 |
[Effective Java] 2-8. 객체 생성과 파괴: 소멸자 사용을 피하라 (3) | 2024.12.27 |
[Effective Java] 2-7. 객체 생성과 파괴: 다 쓴 객체의 참조를 해제하라 (0) | 2024.12.27 |
[Effective Java] 2-6. 객체 생성과 파괴: 불필요한 객체 생성을 피하라 (0) | 2024.12.27 |