본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

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

by 돼지왕 왕돼지 2015. 12. 31.
반응형

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


0.0, 0.0f, ==, assertionerror, consistent, Double, double.compare, double.nan, Effective JAVA, ELEMENT, equals, float, float.compare, float.nan, hashcode, instanceof, Key, map, object, override, package private, Private, reflecive, reflxive, Set, Symmetric, Throw, transitive, [Effective Java] equals 메소드를 오버라이딩 할 때는 보편적 계약을 따르자., 검사, 논리, 다른 타입, 대칭적, 똑똑한 척, 보편적 계약, 비교, 비용, 성능, 슈퍼 클래스, 양질의 equals, 오버라이딩, 유일, 이디엄, 이행적, 인스턴스, 일관성, 일관적, 재귀적, 주의사항, 참조, 참조 비교, 판단, 패키지 전용


-
인스턴스의 동일 여부를 판정하는 equals 메소드의 오버라이딩은 간단한 것 같지만, 잘못되는 경우가 많아서 참담한 결과를 초래할 수 있다.
( 기본 equals 는 참조를 비교한다. )


-
다음의 조건이 만족된다면 슈퍼 클래스의 equals 를 그냥 사용해도 된다.

1. 클래스의 각 인스턴스가 본래부터 유일한 경우.

2. 두 인스턴스가 논리적으로 같은지 검사하지 않아도 되는 클래스의 경우.

3. 수퍼 클래스에서 equals 메소드를 이미 오버라이딩 했고, 그 메소드를 그대로 사용해도 좋은 경우.

4. private 이나 패키지 전용(package private) 클래스라서 이 클래스의 equals 메소드가 절대 호출되지 않아야 할 경우.
( 이 경우에는 AssertionError 를 throw 하는 것이 더 좋다. )


-
equals 는 인스턴스가 갖는 값을 기준으로 논리적으로 같은지 판단할 필요가 있을 때 사용된다.
Map 의 key 나 Set 의 element 로 객체를 저장하고 사용할 수 있게 하려면 equals 메소드의 오버라이딩이 꼭 필요하다.


-
equals 를 오버라이드 할 때 다음 보편적 계약을 따라야 한다. ( null 이 아님에 대해 )

1. 재귀적이다. ( reflexive )
    x.equals(x) == true

2. 대칭적이다. ( symmetric )
    y.equals(x) == x.equals(y)

3. 이행적이다. ( transitive )
    x.equals(y) == y.equals(z) == x.equals(z)

4. 일관적이다. ( consistent )
    x.equals(y) 는 값이 변경되지 않으면 항상 일정

5. x.equals( null ) 은 반드시 false 를 반환.


-
양질의 equals 메소드를 만드는 방법은 다음과 같다.

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

2. instanceof 연산자를 사용해서 전달된 인자가 올바른 타입인지 확인하자. 그렇지 않다면 false 를 반환한다. 클래스가 구현하는 인터페이스도 올바른 타입이 될 수 있다.

3. 인자 타입을 올바른 타입으로 변환한다.

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

float, double 타입이 아닌 기본형은 == 로 비교하고, 필드가 객체일 때는 equals 로 비교한다.

float, double 의 경우 각각 Float.compare 과 Double.compare 로 비교해야 한다.
float 과 double 은 각각 Float.NaN, -0.0f 과 Double.NaN, -0.0 값이 나올 수 있기 때문이다.

5. 참조필드가 null 일 가능성은 항상 발생하는데 다음 이디엄을 사용하면 좋다.
    (field == null ? o.field == null : field.equals(o.field))
    (field == o.field || (field != null && field.equals(o.lfield)) // 성능이 더 좋다.

6. 성능향상을 위한다면, 다를 가능성이 가장 많거나 비교 비용이 적게 드는 필드부터 먼저 비교한다.

7. equals 메소드를 작성한 후에는 대칭적, 이행적, 일관성을 갖는지 확인한다.


-
equals 메소드를 오버라이드 할 때는 hashCode 메소드도 항상 오버라이드 해야 한다.


-
너무 똑똑한 척 해서 쓸데없는 비교나 잘못된 비교를 하기 쉬우니 이것도 조심한다.


-
equals 메소드의 인자 타입을 Object 대신 다른 타입으로 바꾸지 말자.






반응형

댓글