Java:Instanceof和Generics

问题

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

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

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

这是错误消息:

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

有什么更好的方法呢?


#1 热门回答(69 赞)

错误消息说明了一切。在运行时,类型消失了,无法检查它。

你可以通过为你的对象创建工厂来捕获它,如下所示:

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

然后在对象的构造函数存储中输入类型,这样变量使得你的方法看起来像这样:

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

#2 热门回答(32 赞)

使用泛型进行运行时类型检查的两个选项:
选项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(你的泛型类型),只是为了欺骗编译器.Your cast does nothing at runtime,但是当你尝试将错误类型的对象传递给抽象方法时,仍会遇到ClassCastException。

注意1:如果你在抽象方法中执行其他未经检查的强制转换,则会在此处捕获ClassCastExceptions。这可能是好事也可能是坏事,所以请仔细考虑。

注2:You get a free null check when you use instanceof。由于你无法使用它,你可能需要赤手检查空值。


#3 热门回答(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()方法从索引获取其他参数化类型。