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


174

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. 잘못된 생각의 이유가 있습니까?


1
x instanceof SomeClassis 인 경우 표현식 은 false x입니다 null. 따라서 두 번째 구문에는 null 검사가 필요하지 않습니다.
rds

5
@rds 예, 코드 스 니펫 직후의 단락 에도이 내용이 나와 있습니다. 이 코드는 Eclipse에서 생성하기 때문에 코드 스 니펫에 있습니다.
Kip

답변:


100

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


위의 조쉬 블로크 인용문을 읽을 때 정확히 내 마음에 들었습니다. +1 :)
Johannes Schaub-litb

13
확실히 중요한 결정입니다. 대칭이 적용되어야하고 instanceof를 사용하면 실수로 비대칭이되기가 매우 쉽습니다.
Scott Stanchfield

또한 " final제한적일 경우 "(와 둘 다 equals에 해당 hashCode)를 추가 한 다음 계약 의 대칭 및 전이성 요구 사항을 보존하기 위해 getClass()대신 동등성 을 사용 instanceof합니다 equals.
그림자 맨

176

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

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

또한보십시오 이 SO 답변 .

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


18
+1 자바를하는 모든 사람은이 책을 10 번 이상 읽어야합니다!
André

16
instanceof 접근법의 문제점은 x.getClass가 x.equals (y) == true이지만 x.getClass 인 경우 y.equals (x) == false 일 수 있다는 점에서 Object.equals ()의 "symmetric"특성을 깨뜨리는 것입니다. ()! = y.getClass (). 절대적으로 필요한 경우가 아니면 (즉, 관리 대상 또는 프록시 객체의 equals () 재정의)이 속성을 중단하지 않는 것이 좋습니다.
Kevin Sitze 2016 년

8
인스턴스 클래스의 접근 방식은 기본 클래스가 서브 클래스 객체 간의 동등성을 의미하는 경우에만 적합합니다. 사용하여 getClass인스턴스의 종류를 구성 할 수없는 것 - LSP를 단순히 기존 인스턴스로 무엇을 할 수 있는지에 관련이 있기 때문에, LSP를 위반하지 않습니다. 에 의해 반환 된 클래스 getClass는 객체 인스턴스의 불변 속성입니다. LSP는 해당 특성이 해당 클래스를 작성한 클래스 이외의 클래스를 나타내는 서브 클래스를 작성할 수 있음을 의미하지 않습니다.
supercat

66

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

결론

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

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

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

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


61

사용하는 이유 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

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

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


8
이것이이 질문에 대한 첫 번째 명확하고 간결한 답변입니다. 코드 샘플은 천 단어의 가치가 있습니다.
Johnride

나는 당신이 하루를 구한 다른 모든 자세한 답변을 겪고있었습니다.

1
언급 한대로 대칭 요구 사항이 있습니다. 그러나 "과도 성"요구 사항도 있습니다. 만약 a.equals(c)b.equals(c)다음 a.equals(b)(만들기의 본래의 접근법은 Dog.equals단지 return super.equals(object)!(object instanceof Dog)지만 대칭을 위반하지 것이지만, 이행 성을 위반하는 것 개 인스턴스 때 추가 필드를 확인)
그림자 남자

안전을 위해 getClass()평등 검사를 사용 instanceof하거나 equalsand and hashCodemethods 이면 검사를 사용할 수 있습니다 final.
Shadow Man

26

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

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

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

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

1
getClass기본 클래스가 다른 서브 클래스의 인스턴스를 동일하게 비교할 수있는 방법을 구체적으로 문서화하지 않았다면 사용에 관한 어떠한 것도 LSP를 위반하지 않습니다. 어떤 LSP 위반이 보입니까?
supercat

아마도 getClass 사용 으로 다시 정의해야하며 하위 유형을 LSP 위반의 위험이 있습니다. Bloch에는 Effective Java의 예가 있습니다 . 여기 에서도 설명 합니다 . 그의 근거는 주로 상태를 추가하지 않는 하위 유형을 구현하는 개발자에게 놀라운 행동을 초래할 수 있다는 것입니다. 나는 당신의 요점을 이해합니다-문서가 핵심입니다.
McDowell

2
고유 클래스의 두 인스턴스가 동일하게보고해야하는 유일한 시간은 공통 기본 클래스 또는 인터페이스가 동일성을 의미하는 것을 정의하는 인터페이스 (자바가 IMHO, 일부 콜렉션 인터페이스에 이중 적용한 철학)를 상속하는 경우뿐입니다. 기본 클래스 계약 getClass()에 문제의 클래스가 기본 클래스로 변환 가능하다는 사실을 넘어 의미가 있다고 간주되지 않는 한, 반환은 getClass()인스턴스가 같도록 일치해야하는 다른 속성과 마찬가지로 고려해야합니다.
supercat

1
@eyalzba subclass가 equals 계약에 구성원을 추가 한 경우 instanceof_가 사용되는 경우 대칭 평등 요구 사항을 위반합니다 . getClass 서브 클래스를 사용 하는 것은 절대 상위 유형과 같을 수 없습니다. 어쨌든 재정의해야한다는 것이 아니라, 이것이 있다면 절충입니다.
McDowell

2
"상속을위한 디자인 및 문서화 또는 금지"나는 매우 중요하다고 생각합니다. 내 이해는 불변 값 유형을 만드는 경우에만 클래스를 final로 선언해야한다는 것입니다. 상속이 없으므로 필요에 따라 동등하게 구현할 수 있습니다. 상속을 허용하는 경우 객체는 참조 동등성으로 비교해야합니다.
TheSecretSquad

23

내가 틀렸다면 정정하십시오. 그러나 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

나를 위해 이것이 이유입니다
karlihnos

5

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

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

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

두 번째 요점 : 그는 질문에서 이미 언급했습니다. "보통 instanceof 옵션을 확인한 다음 'if (obj == null)'확인을 제거하십시오."
마이클 마이어스

5

주어진 클래스의 서브 클래스가 부모 클래스와 같은지 고려하는지 여부에 따라 다릅니다.

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

여기서는 LastName을 FamilyName과 비교하기를 원하기 때문에 'instanceof'를 사용합니다.

class Organism
{
}

class Gorilla extends Organism
{
}

클래스가 이미 두 인스턴스가 동일하지 않다고 말했기 때문에 여기서는 'getClass'를 사용합니다.


3

instanceof 는 동일한 클래스 또는 그 서브 클래스의 인스턴스에 작동합니다

객체를 사용하여 클래스의 인스턴스, 서브 클래스의 인스턴스 또는 특정 인터페이스를 구현하는 클래스의 인스턴스인지 테스트 할 수 있습니다.

ArryaList와의 RoleList는 모두 instanceof를 목록을

동안

getClass () == o.getClass () 는 두 객체 (this 및 o)가 정확히 동일한 클래스에 속하는 경우에만 true입니다.

따라서 비교해야 할 내용에 따라 둘 중 하나를 사용할 수 있습니다.

논리가 "하나의 객체가 모두 같은 클래스 인 경우에만 다른 객체와 같다"는 경우, "등호"를 선택해야합니다. 대부분의 경우입니다.


3

두 방법 모두 문제가 있습니다.

서브 클래스가 ID를 변경하면 실제 클래스를 비교해야합니다. 그렇지 않으면 대칭 특성을 위반하게됩니다. 예를 들어, 서로 다른 유형의 Persons가 동일한 이름을 갖더라도 동등한 것으로 간주되어서는 안됩니다.

그러나 일부 서브 클래스는 ID를 변경하지 않으며이를 사용해야 instanceof합니다. 예를 들어, 불변의 Shape객체 가 많은 경우 Rectangle길이와 너비가 1 인 단위는 단위와 같아야합니다 Square.

실제로, 나는 전자의 경우가 더 사실이라고 생각합니다. 일반적으로 서브 클래 싱은 정체성의 기본 요소이며, 한 가지 작은 일을 할 수 있다는 점을 제외하고는 부모와 똑같다는 것은 평등하지 않습니다.


-1

실제로 instanceof는 객체가 어떤 계층에 속하는지 확인합니다. 예 : Car 객체는 Vehical 클래스에 속합니다. 따라서 "new Car () instance of Vehical"은 true를 반환합니다. Car 객체는 Vehical 클래스에 속하지만 별도의 유형으로 분류되지만 "new Car (). getClass (). equals (Vehical.class)"는 false를 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.