backend/Java

[Effective Java] 8. equals 메소드를 오버라이딩 할 때는 보편적 계약을 따르자.

버리야 2009. 9. 24. 11:07
반응형
8. equals 메소드를 오버라이딩 할 때는 보편적 계약을 따르자.


equals메소드를 오버라이드 하지 않고 써야할때
- 클래스의 각 인스턴스가 본래부터 유일한 경우
- 두 인스턴스가 논리적으로 같은지 검사하지 않아도 되는 클래스의 경우
- 수퍼 클래스에서 equals를 이미 오버라이딩 했고, 그 메소드를 그대로 사용해도 좋은 경우
- private나 패키지 전용 클래스라서 이 클래스의 equals 메소드가 절대 호출되지 않아야 할 경우

그럼 언제 해야하나?

객체 참조만으로 인스턴스의 동일 여부를 판단하는 것이 아니라, 인스턴스가 갖는 값을 비교하여 논리적으로 같은지 판단할 필요가 있는 클래스로써,
자신의 수퍼클래스에서 equals 메소드를 오버라이드하지 않았을 때다. 일반적으로 값(value) 클래스가 해당된다.
객체의 참조 여부보다 객체가 갖는 값이 논리적으로 같은지가 관심사일때, 또한 Map의 키나 Set의 요소로 객체를 저장하고 사용할 수 있게 하려면 equals 메소드의
오버라이딩이 꼭 필요하다. 같은 객체가 이미 있는지 비교하는 수단을 제공해야 하기 때문이다.

equals 메소드를 오버라이드 할 필요 없는 값 클래스는 각 값 당 최대 하나의 객체만 존재하도록 인스턴스 제어를 사용하는 클래스
열거형이 그런 부류에 속하는데 그런 클래스들의 경우는 논리적인 일치와 객체 참조 일치가 동일한 의미가 된다.

equals 메소드를 만드는 방법

1) 객체의 값을 비교할 필요 없고 참조만으로 같은 객체인지 비교 가능하다면 == 연산자를 사용하자. 

2) instanceof 연산자를 사용해서 전달된 인자가 올바른 타입인지 확인하자.

대개의 경우 올바른 타입이란 호출된 equals 메소드가 정의된 클래스를 말하지만, 그 클래스가 구현하는 인터페이스도 올바른 타입이 될 수 있다. 
따라서 어떤 인터페이스를 구현하는 클래스들이 여러 개 있고, 각 클래스들은 equals 계약에 맞게 equals 메소드를 오버라이딩 하고 있다면, 
그 인터페이스 타입으로 각 클래스들의 객체를 비교할 수 있다. 예를 들어 Set, List, Map, Map.Entry 같은 컬렉션 인터페이스가 그런 형태이다.

3) 인자 타입을 올바른 타입으로 변환한다. 이러한 타입 변환은 instanceof 검사 후에 행하게 되므로 예외가 생기지 않고 안전하게 처리된다.

4) 클래스의 "중요한(꼭 비교해야 하는)" 필드 각각에 대해서는 인자로 전달된 객체의 필드와 현재 객체(equals 메소드가 호출된)의 필드가 모두 같은지 빠뜨리지 말고 비교한다.

5) equals 메소드가 대칭적이며 이행적이며 일관성이 있는지 확인한다.
대칭적(Symmetric) : y.equals(x) 가 true이면, x.equals(y)도 true 여야한다.
이행적(Transitive) : x.equals(y) 가 true이면, y.equals(z)가 true이면 x.equals(z)도 true
일관성(Consistent) : x.equals(y)를 여러번 호출해도 일관성있게 값을 리턴해야한다.


유의점.
equals 메소드를 오버라이드 할 땐 hashCode 메소드도 항상 오버라이드 한다.
너무 지나치게 동일 여부를 비교하려는건 좀... 동일 파일을 참조하는  심볼릭 링크 객체들이 같은지 File 클래스의 equals 에서 비교하려고 하면 안 된다.
equals 메소드의 인자 타입을 Object 대신 다른 타입으로 바꾸지 말자.



반응형