[java] .equals ()를 생성 할 때 instanceof보다 getClass ()를 선호하는 이유는 무엇입니까?

Eclipse를 사용하여 .equals()및 을 생성 하고 .hashCode()있으며 ”instanceof ‘를 사용하여 유형 비교’라는 옵션이 있습니다. 기본값은이 옵션을 선택하지 않고 .getClass()유형을 비교 하는 데 사용 됩니다. 내가 선호해야 할 이유 .getClass()instanceof있습니까?

사용하지 않고 instanceof:

if (obj == null)
  return false;
if (getClass() != obj.getClass())
  return false;

사용 instanceof:

if (obj == null)
  return false;
if (!(obj instanceof MyClass))
  return false;

일반적으로 instanceof옵션을 확인한 다음 ” if (obj == null)“확인으로 이동합니다. null 객체는 항상 실패하기 때문에 중복됩니다 instanceof. 잘못된 생각의 이유가 있습니까?



답변

당신이 사용하는 경우 instanceof, 당신하고 equals구현하는 final방법의 대칭 계약을 유지합니다 x.equals(y) == y.equals(x). final제한적으로 보이는 경우 , 객체 구현에 대한 개념을주의 깊게 검토하여 대체 구현이 Object클래스가 설정 한 계약을 완전히 유지하는지 확인하십시오 .


답변

Josh Bloch는 귀하의 접근 방식을 선호합니다.

instanceof접근 방식을 선호하는 이유는 접근 방식을 사용할 때 getClass객체가 동일한 런타임 유형의 동일한 클래스의 다른 객체와 동일하다는 제한이 있기 때문입니다. 클래스를 확장하고 몇 가지 무해한 메소드를 추가하면 서브 클래스의 일부 객체가 수퍼 클래스의 객체와 같은지 확인하십시오. 모든 중요한 측면에서 객체가 동일한 경우에도 그들이 같지 않다는 놀라운 대답. 실제로 이것은 Liskov 대체 원칙에 대한 엄격한 해석을 위반하며 매우 놀라운 행동을 유발할 수 있습니다. Java에서는 대부분의 컬렉션 (HashTable등)은 equals 방법을 기반으로합니다. 수퍼 클래스의 멤버를 해시 테이블에 키로 넣은 다음 서브 클래스 인스턴스를 사용하여 검색하면 동일하지 않으므로 찾을 수 없습니다.

또한보십시오 이 SO 답변 .

효과적인 Java 3 장 에서도 이에 대해 다룹니다.


답변

Angelika Langers 의 동등한 비밀은 Josh Bloch와 Barbara Liskov가 포함하여 일반적이고 잘 알려진 몇 가지 예에 대해 길고 자세한 토론을 통해 대부분의 문제를 발견합니다. 그녀는 또한 instanceof대에 들어 getClass갑니다. 그것의 일부 인용

결론

equals () 구현에 대해 임의로 선택된 4 개의 예제를 해부 한 후 무엇을 결론을 내립니까?

우선 : equals () 구현에서 유형 일치 검사를 수행하는 두 가지 방법이 있습니다. 클래스는 instanceof 연산자를 사용하여 수퍼 클래스와 서브 클래스 객체 간의 혼합 유형 비교를 허용하거나 클래스가 getClass () 테스트를 통해 다른 유형의 객체를 동일하지 않은 것으로 처리 할 수 ​​있습니다. 위의 예제는 getClass ()를 사용하는 equals () 구현이 일반적으로 instanceof 사용하는 구현보다 더 강력하다는 것을 잘 보여줍니다.

instanceof 테스트는 최종 클래스에 대해서만 또는 수퍼 클래스에서 적어도 equals () 메소드가 final 인 경우에만 정확합니다. 후자는 본질적으로 서브 클래스가 수퍼 클래스의 상태를 확장해서는 안된다는 것을 의미하지만 과도 또는 정적 필드와 같이 객체의 상태 및 동작과 관련이없는 기능이나 필드 만 추가 할 수 있습니다.

반면에 getClass () 테스트를 사용하는 구현은 항상 equals () 계약을 준수합니다. 그들은 정확하고 강력합니다. 그러나 테스트의 인스턴스를 사용하는 구현과 의미 상 매우 다릅니다. getClass ()를 사용하는 구현에서는 서브 클래스가 필드를 추가하지 않고 equals ()를 대체하지 않더라도 서브 클래스와 수퍼 클래스 오브젝트를 비교할 수 없습니다. 이러한 “사소한”클래스 확장은 예를 들어 정확히 “사소한”목적으로 정의 된 서브 클래스에 디버그 인쇄 메소드를 추가하는 것입니다. 수퍼 클래스가 getClass () 점검을 통한 혼합 유형 비교를 금지하면, 사소한 확장은 수퍼 클래스와 비교할 수 없습니다. 이것이 문제인지의 여부는 클래스의 의미와 확장의 목적에 달려 있습니다.


답변

사용하는 이유 getClassequals계약 의 대칭 특성을 보장하기위한 것 입니다. 동등한 JavaDoc에서 :

대칭입니다. null이 아닌 참조 값 x 및 y의 경우, y.equals (x)가 true를 반환하는 경우에만 x.equals (y)가 true를 반환해야합니다.

instanceof를 사용하면 대칭 적이 지 않을 수 있습니다. 예를 들어 보자 : 개는 동물을 확장한다. 동물의는 equals않는 instanceof동물의 확인. 개는 equals않는 instanceof개 확인. Animal a 와 Dog d (다른 필드와 동일)를 제공하십시오.

a.equals(d) --> true
d.equals(a) --> false

이는 대칭 특성을 위반합니다.

평등의 계약을 엄격하게 준수하려면 대칭이 보장되어야하므로 클래스가 동일해야합니다.


답변

이것은 종교적인 논쟁의 일부입니다. 두 방법 모두 문제가 있습니다.

  • instanceof를 사용 하면 하위 클래스에 중요한 멤버를 추가 할 수 없습니다.
  • getClass를 사용 하면 Liskov 대체 원칙을 위반합니다.

BlochEffective Java Second Edition 에서 관련 조언을 제공합니다 .

  • 항목 17 : 상속을위한 디자인 및 문서화 또는 금지

답변

내가 틀렸다면 정정하십시오. 그러나 getClass ()는 인스턴스가 비교하는 클래스의 하위 클래스가 아닌지 확인하려는 경우 유용합니다. 해당 상황에서 instanceof를 사용하면 다음과 같은 이유로 알 수 없습니다.

class A { }

class B extends A { }

Object oA = new A();
Object oB = new B();

oA instanceof A => true
oA instanceof B => false
oB instanceof A => true // <================ HERE
oB instanceof B => true

oA.getClass().equals(A.class) => true
oA.getClass().equals(B.class) => false
oB.getClass().equals(A.class) => false // <===============HERE
oB.getClass().equals(B.class) => true


답변

해당 클래스 만 일치 시키려면을 사용하십시오 getClass() ==. 서브 클래스를 일치 시키려면 instanceof필요합니다.

또한 instanceof는 null과 일치하지 않지만 null과 비교하는 것이 안전합니다. 따라서 null을 확인할 필요가 없습니다.

if ( ! (obj instanceof MyClass) ) { return false; }