diff --git "a/book-effective-java/2\354\236\245_\352\260\235\354\262\264_\354\203\235\354\204\261\352\263\274_\355\214\214\352\264\264/\352\271\200\353\257\274\354\243\274/2-1_\354\240\225\354\240\201\355\214\251\355\204\260\353\246\254\353\251\224\354\204\234\353\223\234.md" "b/book-effective-java/2\354\236\245_\352\260\235\354\262\264_\354\203\235\354\204\261\352\263\274_\355\214\214\352\264\264/\352\271\200\353\257\274\354\243\274/2-1_\354\240\225\354\240\201\355\214\251\355\204\260\353\246\254\353\251\224\354\204\234\353\223\234.md" index 225a9e9..a1f6eac 100644 --- "a/book-effective-java/2\354\236\245_\352\260\235\354\262\264_\354\203\235\354\204\261\352\263\274_\355\214\214\352\264\264/\352\271\200\353\257\274\354\243\274/2-1_\354\240\225\354\240\201\355\214\251\355\204\260\353\246\254\353\251\224\354\204\234\353\223\234.md" +++ "b/book-effective-java/2\354\236\245_\352\260\235\354\262\264_\354\203\235\354\204\261\352\263\274_\355\214\214\352\264\264/\352\271\200\353\257\274\354\243\274/2-1_\354\240\225\354\240\201\355\214\251\355\204\260\353\246\254\353\251\224\354\204\234\353\223\234.md" @@ -118,7 +118,7 @@ public class House { - `BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);` - `instance`, `getInstance` : 매개변수로 명시한 인스턴스 반환, 같은 인스턴스임은 보장되지 않는다. - `StackWalker luke = StackWalker.getInstance(options);` -- `create`, `newInstance` : `instance`, `getInstance`와 같지만, 매ㄴ 새로운 인스턴스를 생성해 반환함을 보장한다. +- `create`, `newInstance` : `instance`, `getInstance`와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장한다. - `Object newArray = Array.newInstance(classObject, arrayLen);` - `get[type]` : `getInstance`와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. - `FileStore fs = Files.getFileStore(path);` diff --git "a/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-10_equals.md" "b/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-10_equals.md" new file mode 100644 index 0000000..2ec4ba2 --- /dev/null +++ "b/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-10_equals.md" @@ -0,0 +1,90 @@ +## 아이템 10. `equals`는 일반 규약을 지켜 재정의하라 + +> 꼭 필요한 경우가 아니면 `equals`를 재정의하지 말자. 해야한다면 5가지 규약을 꼭 지키자. + +### `equals`를 재정의하지 않는 상황들 + +#### 1. 각 인스턴스가 본질적으로 고유하다. + +- 값을 표현하는 게 아니라 동작하는 개체를 표현하는 클래스가 이에 해당한다. + - `Thread` + +#### 2. 인스턴스의 논리적 동치성(logical equality)를 검사할 일이 없다. + +#### 3. 상위 클래스에서 정의한 `equals`가 하위 클래스에도 딱 들어맞을 때 + +#### 4. 클래스가 `private`이거나 `package-private`이고 `equals`를 호출할 일이 없을 때 + +- `equals`의 호출을 막기 위해서 다음과 같이 코드를 작성해도 좋다. + +```java +@Override +public boolean equals(Object o) { + throw new AssertionError(); +} +``` + +### `equals`를 재정의해야 하는 상황들 + +- 객체 식별성(object identity; 두 객체가 물리적으로 같은지)이 아니라 논리적 동치성을 확인해야하는 필요가 있을 때 + - 주로 값 클래스 + - `Integer`, `String` ... + - 값이 같은 인스턴스가 둘 이상 만들어지지 않음이 보장되는 인스턴스 통제 클래스는 제외 + +### `equals` 재정의 규약 + +#### 반사성(reflexivity) + +> null이 아닌 모든 참조 값 x에 대해, `x.equals(x)`는 `true`이다. + +#### 대칭성(symmetry) + +> null이 아닌 모든 참조 값 x, y에 대해, `x.equals(y)`가 `true`면 `y.equals(x)`도 `true`다. + +#### 추이성(transitivitiy) + +> 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`를 반환한다. + +- `equals`의 판단에 신뢰할 수 없는 자원이 끼어들게 해서는 안된다. +- `equals`는 항시 메모리에 존재하는 객체만을 사용한 결정적 계산만 수행해야 한다. + +#### null 아님 + +> null이 아닌 모든 참조 값 x에 대해, `x.equals(null)`은 `false`다. + +- `instanceof`에서 `null`도 걸러진다. + +### `equals` 구현 절차 + +#### 1. `==` 연산자를 사용하여 입력이 자기 자신의 참조인지 확인 + +- 성능 최적화용 + +#### 2. `instanceof` 연산자로 입력이 올바른 타입인지 확인 + +- 클래스가 구현한 특정 인터페이스일 수 있다. + +#### 3. 입력을 올바른 타입으로 형변환 + +- 앞서 타입 확인을 했기 때문에 무조건 성공 + +#### 4. 입력 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치하는지 하나씩 검사한다. + +- `float`과 `double`은 각각 정적 메서드인 `Float.compare(float, float)`, `Double.compare(double, double)`로 비교 + - 부동 소수점 +- `null`을 정상값으로 취급하는 경우 `Objects.equals(Object, Object)`를 사용하여 `NullPointerException` 예방 + +### 고려해야 할 점 + +- 어떤 필드를 먼저 비교하느냐가 `equals`의 성능을 좌우할 때가 있다. + - 다를 가능성이 더 크거나 비교하는 비용이 싼 필드를 먼저 비교하는 것이 좋다. +- 동기화용 락(lock) 필드 같이 객체의 논리적 상태와 관련 없는 필드는 비교하면 안된다. +- `equals`를 재정의했다면 단위 테스트를 해보자. +- `hashCode`도 같이 재정의해라. +- `Object` 외의 타입을 매개변수로 받는 `equals`는 선언하지 말자. + - 재정의가 아니라 다중정의이다. +- 구글의 오픈소스 프레임워크 AutoValue를 사용하면 어노테이션 하나로 메서드들을 작성해준다. 또는 IDE에 맡겨도 좋다. diff --git "a/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-11_hashCode.md" "b/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-11_hashCode.md" new file mode 100644 index 0000000..c1fefaf --- /dev/null +++ "b/book-effective-java/3\354\236\245_\353\252\250\353\223\240_\352\260\235\354\262\264\354\235\230_\352\263\265\355\206\265_\353\251\224\354\204\234\353\223\234/\352\271\200\353\257\274\354\243\274/3-11_hashCode.md" @@ -0,0 +1,40 @@ +## 아이템 11. `equals`를 재정의하려거든 `hashCode`도 재정의하라 + +> equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다. + +- 위 규약은 논리적으로 같은 객체는 같은 해시코드를 반환해야 한다는 것이다. +- `hashCode`를 재정의하지 않는다면 `Object`의 기본 `hashCode`는 논리적으로 같은 두 객체를 다르다고 판단하여 서로 다른 값을 반환한다. + +### 올바른 해시코드 구현 + +#### 잘못된 예시 + +```java +@Override public int hashCode() { return 42; } +``` + +- 모든 객체에 같은 값을 넘겨주게 되면 실제로는 해시 테이블이 연결 리스트처럼 동작한다. + - 평균 수행 시간 : O(n) + +#### 좋은 `hashCode` 작성 요령 + +1. `int` 변수 `result`를 선언한 후 값 `c`로 초기화 +2. 해당 객체의 나머지 핵심 필드 `f` 각각에 다음 작업을 수행한다. + 1. 해당 필드의 해시코드 `c`를 계산 + 2. 계산한 해시코드 `c`로 `result`를 갱신 (`result = 31 * result + c`) +3. `result` 반환 + +- 다른 필드로부터 계산해낼 수 있는 필드(파생 필드)는 제외해도 된다. +- `equals`에 사용되지 않은 필드는 **반드시** 제거해야 한다. +- `31 * result`는 곱하는 순서에 따라서 `result` 값이 달라진다. + + - 31인 이유는 홀수이면서 소수이기 때문이다. + - 2를 곱하는 것은 시프트 연산과 같은 결과를 내기 때문에 홀수로 해야한다. + - 소수인 것은 전통적으로 그래왔다. + - `31 * i`는 `(i << 5) - i`로 최적화할 수 있다. + +- 클래스가 불변이고 해시코드를 계산하는 비용이 크다면 캐싱을 고려하자. +- **성능을 개선한다고 핵심 필드를 생략해서는 안된다.** +- **`hashCode` 생성 규칙을 API 사용자에게 자세히 공표하지 말자.** + - 클라이언트가 값에 의지하지 않음 + - 추후에 계산 방식을 바꿀 수 있음