자바 : Instanceof와 Generics


135

값의 인덱스에 대한 일반적인 데이터 구조를 살펴보기 전에 유형의 인스턴스조차 this매개 변수화되어 있는지 확인하고 싶습니다 .

그러나 Eclipse는 내가 이것을 할 때 불평합니다.

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }

이것은 오류 메시지입니다.

유형 매개 변수 E에 대해 instanceof check를 수행 할 수 없습니다. 런타임에 일반 유형 정보가 지워 지므로 삭제 오브젝트를 대신 사용하십시오.

더 좋은 방법은 무엇입니까?

답변:


79

오류 메시지에 모두 나와 있습니다. 런타임에 유형이 사라 졌으므로 확인할 방법이 없습니다.

다음과 같이 객체를위한 팩토리를 만들어서 잡을 수 있습니다.

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

그런 다음 객체의 생성자에 해당 유형을 저장하므로 메소드가 다음과 같이 보일 수 있습니다.

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }

3
난 당신이 원하는 것 같아요 Class.isAssignableFrom.
Tom Hawtin-tackline

@Tom, 나는 지난 밤에 기억에서 이것을 썼고, 실제로 그것을 수업에 통과시키기 위해 고쳤지만 (duh!), 그렇지 않으면 왜 당신이 그것을 원하지 않을지 이해하지 못합니다 (오늘 아침에 커피가 더 필요할지 모릅니다) 첫 번째 컵에만 m).
Yishai

나는 톰과 함께 있습니다. 당신은 그것을 명확히 할 수 있습니까? isAssignableFrom ()을 사용하는 것이 직업을 선택한 것입니다. 어쩌면 내가 뭔가를 놓치고 있습니까?
luis.espinal

6
@ luis, Tom의 의견은 아마도 isInstance ()를 사용하고 실제 arg0 매개 변수를 전달해야 함을 의미했을 것입니다. 널 검사를 피할 수 있다는 장점이 있습니다.
Yishai

1
비슷한 상황이지만 대답을 완전히 이해할 수 없습니다. 나와 같은 더미에 대해 더 잘 설명해 주시겠습니까?
Rubens Mariuzzo

40

제네릭을 사용한 런타임 유형 검사를위한 두 가지 옵션 :

옵션 1-생성자 손상

indexOf (...)를 재정의하고 전체 컬렉션을 반복하는 자신을 저장하기 위해 성능을 위해 유형을 확인하려고한다고 가정 해 봅시다.

다음과 같이 더러운 생성자를 만드십시오.

public MyCollection<T>(Class<T> t) {

    this.t = t;
}

그런 다음 isAssignableFrom 을 사용 하여 유형을 확인할 수 있습니다 .

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

객체를 인스턴스화 할 때마다 스스로 반복해야합니다.

new MyCollection<Apples>(Apples.class);

가치가 없다고 결정할 수도 있습니다. ArrayList.indexOf (...) 구현 에서 형식이 일치하는지 확인하지 않습니다.

옵션 2-실패하자

알 수없는 유형이 필요한 추상 메소드를 사용해야하는 경우 실제로 원하는 것은 컴파일러가 instanceof 에 대한 울음을 멈추는 것 입니다. 다음과 같은 방법이 있다면 :

protected abstract void abstractMethod(T element);

다음과 같이 사용할 수 있습니다.

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

컴파일러를 속이기 위해 객체를 T (일반 유형)로 캐스팅하고 있습니다. 캐스트는 런타임에 아무것도 수행하지 않지만 잘못된 유형의 객체를 추상 메소드에 전달하려고하면 여전히 ClassCastException이 발생합니다.

참고 1 : 추상 메소드에서 확인되지 않은 추가 캐스트를 수행하는 경우 여기에서 ClassCastException이 발생합니다. 그것은 좋거나 나쁠 수 있으므로 생각하십시오.

참고 2 : instanceof를 사용할 때 무료 null 검사가 제공됩니다 . 사용할 수 없으므로 맨손으로 null을 확인해야 할 수도 있습니다.


18

오래된 게시물이지만 일반적인 instanceOf 검사를 수행하는 간단한 방법입니다.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}

21
실제로 여기에 무엇을 제공하고 있는지 명확하지 않습니다.
Andrew

12

클래스가 일반 매개 변수를 사용하여 클래스를 확장하면 런타임에 리플렉션을 통해이를 얻은 다음 비교를 위해 사용할 수 있습니다.

class YourClass extends SomeOtherClass<String>
{

   private Class<?> clazz;

   public Class<?> getParameterizedClass()
   {
      if(clazz == null)
      {
         ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
          clazz = (Class<?>)pt.getActualTypeArguments()[0];
       }
       return clazz;
    }
}

위의 경우 런타임에 getParameterizedClass ()에서 String.class를 가져 오며 캐시되므로 여러 검사시 리플렉션 오버 헤드가 발생하지 않습니다. ParameterizedType.getActualTypeArguments () 메소드에서 색인으로 다른 매개 변수화 된 유형을 얻을 수 있습니다.


7

나는 같은 문제가 있었고 여기 내 해결책이 있습니다 (매우 겸손한 @george : 이번에는 컴파일하고 작동합니다 ...).

내 probem은 Observer를 구현하는 추상 클래스 안에있었습니다. Observable은 모든 종류의 Object가 될 수있는 Object 클래스로 update (...) 메소드를 실행합니다.

T 타입의 객체 만 처리하고 싶습니다.

해결책은 런타임에 유형을 비교할 수 있도록 클래스를 생성자에 전달하는 것입니다.

public abstract class AbstractOne<T> implements Observer {

  private Class<T> tClass;
    public AbstractOne(Class<T> clazz) {
    tClass = clazz;
  }

  @Override
  public void update(Observable o, Object arg) {
    if (tClass.isInstance(arg)) {
      // Here I am, arg has the type T
      foo((T) arg);
    }
  }

  public abstract foo(T t);

}

구현을 위해 클래스를 생성자에게 전달하면됩니다.

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}

5

또는 실패한 E 시도를 시도 할 수 있습니다.

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}

1
+1 간단한 점검을위한 실용적인 오버 오버 엔지니어링 솔루션! 나는 이것을 더 투표 할 수 있으면 좋겠다
higuaro

24
이 작동하지 않습니다. E는 런타임에 지워 지므로 캐스트가 실패하지 않으며 컴파일러 경고 만 표시됩니다.
Yishai

1

기술적으로는 이것이 제네릭의 요점 일 필요는 없으므로 컴파일 유형 검사를 수행 할 수 있습니다.

public int indexOf(E arg0) {
   ...
}

그러나 클래스 계층 구조가 있으면 @Override가 문제가 될 수 있습니다. 그렇지 않으면 Yishai의 답변을 참조하십시오.


예, List 인터페이스는 함수가 객체 매개 변수를 취하도록 요구합니다.
Nick Heiner

List를 구현하고 있습니까? 왜 List <E>를 구현하지 않습니까?
Jason S


@Rosarch : List 인터페이스의 indexOf () 메소드는 주어진 인수가 찾고있는 객체와 동일한 유형일 것을 요구하지 않습니다. 단지 .equals () 여야하며 다른 유형의 객체는 서로 .equals () 일 수 있습니다. 이것은 remove () 메소드와 동일한 문제입니다. stackoverflow.com/questions/104799/…
newacct

1
[[(sheepishly]]]) 신경 쓰지 마세요. indexOf ()는 Object가 아닌 E로 매개 변수로 필요하다고 생각했습니다. (왜 그들이 그렇게 했습니까?! ??!)
Jason S

1

객체의 런타임 유형은 필터링 할 비교적 임의적 인 조건입니다. 그런 멍청함을 당신의 컬렉션에서 멀리하는 것이 좋습니다. 이것은 단순히 컬렉션에 필터를 대리하여 구성에 전달함으로써 달성됩니다.

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );

(사용 중이 Comparator거나 유사한 경우 인스턴스 유형 검사를 수행 할 수도 있지만 )
Tom Hawtin-tackline
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.