155

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

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

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

这是错误消息:

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

更好的方法是什么?

4

9 回答 9

83

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

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

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

然后在对象的构造函数中存储该类型,如此可变,以便您的方法看起来像这样:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
于 2009-10-15T03:10:39.007 回答
46

使用泛型进行运行时类型检查的两个选项:

选项 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 。

于 2013-06-12T17:53:18.590 回答
21

旧帖子,但一种简单的方法来进行通用 instanceOf 检查。

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
于 2015-02-18T10:04:33.417 回答
14

如果您的类扩展了具有泛型参数的类,您也可以在运行时通过反射获得它,然后使用它进行比较,即

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() 方法的索引获取其他参数化类型。

于 2011-07-09T00:10:51.227 回答
7

我遇到了同样的问题,这是我的解决方案(非常谦虚,@george:这次编译和工作......)。

我的探针位于实现 Observer 的抽象类中。Observable 触发方法 update(...) 与可以是任何类型的 Object 的 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){
  }
}
于 2014-11-15T11:41:22.420 回答
6

或者你可以捕捉到一个失败的尝试投射到 E例如。

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
于 2012-10-28T05:29:00.517 回答
1

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

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

但是如果你有一个类层次结构,那么@Override 可能是一个问题。否则请参阅一晒的回答。

于 2009-10-15T03:13:25.363 回答
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;
             }
         }}
     );
于 2009-10-15T04:08:29.083 回答
0

让Java确定它并捕获异常底线。

public class Behaviour<T> {
    public void behave(Object object) {
        T typedObject = null;
        
        try { typedObject = (T) object; }
        catch (ClassCastException ignored) {}
        
        if (null != typedObject) {
            // Do something type-safe with typedObject
        }
    }
}
于 2021-03-17T19:32:52.213 回答