[java] 자바 : Instanceof와 Generics

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

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

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

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

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

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



답변

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

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

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

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

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


답변

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

옵션 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을 확인해야 할 수도 있습니다.


답변

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

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


답변

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

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 () 메소드에서 색인으로 다른 매개 변수화 된 유형을 얻을 수 있습니다.


답변

나는 같은 문제가 있었고 여기 내 해결책이 있습니다 (매우 겸손한 @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){
  }
}


답변

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

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


답변

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

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

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