Java:Instanceof和泛型


135

在查看通用数据结构中的值索引之前,我想看看它是否甚至this已被参数化为该类型的实例。

但是当我这样做时,Eclipse会抱怨:

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

这是错误消息:

无法针对类型参数E执行instanceof检查。请改用其擦除对象,因为泛型类型信息将在运行时删除

有什么更好的方法呢?

Answers:


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,我昨晚是从记忆中写这本书的,我将其固定为实际通过了课程(duh!),但否则,我不明白为什么您不想要它(也许今天早上我需要更多咖啡,我只在我的第一个杯子上)。
Yishai

我和汤姆在一起。你能澄清一下吗?使用isAssignableFrom()将是我的工作选择。也许我想念什么?
luis.espinal

6
@ luis,Tom的评论可能意味着我应该使用isInstance()并传递实际的arg0参数。它具有避免空检查的优点。
Yishai 2010年

1
我有类似的情况,但我无法完全理解答案。您能为像我这样的假人澄清一下吗?
Rubens Mariuzzo 2011年

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:如果您在抽象方法中执行其他未经检查的强制转换,则ClassCastExceptions将在此处被捕获。那可能是好事也可能是坏事,因此请仔细考虑。

注意2:使用instanceof时,您将获得免费的空检查。由于您无法使用它,因此您可能需要赤手检查null。


18

旧帖子,但是做普通instanceOf检查的一种简单方法。

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

21
目前尚不清楚您实际上在这里贡献什么。
安德鲁

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:这一次编译并正在工作……)。

我的探针位于实现Observer的抽象类中。Observable可以使用Object类触发update(...)方法,该类可以是任何种类的Object。

我只想处理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);

}

对于实现,我们只需要将Class传递给构造函数

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

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

5

否则您可能会尝试将E转换为E失败

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

1
+1务实的非过度设计解决方案,可轻松进行检查!我希望我能再投票给更多人
higuaro 2012年

24
这行不通。E在运行时被擦除,因此强制转换不会失败,您只会收到编译器警告。
Yishai 2013年

1

从技术上讲,您不必这样做,这就是泛型的重点,因此您可以进行编译类型检查:

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

但是如果您有一个类层次结构,那么@Override可能是一个问题。否则,请参见Yishai的答案。


是的,List接口要求函数使用对象参数。
尼克·海纳尔

您正在实施列表?为什么不实现List <E>?
杰森S


@Rosarch:List接口的indexOf()方法不需要给定的参数与您要查找的对象具有相同的类型。它只是必须等于.equals(),并且不同类型的对象可以彼此等于.equals()。这与remove()方法的问题相同,请参见:stackoverflow.com/questions/104799/…–
newacct

1
[[[sheepishly]]]没关系,我认为indexOf()要求E作为参数而不是Object。(为什么这么做?!?!!)
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-09年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.