Java:Instanceof和generics

在我通过generics数据结构查看值的索引之前,我想看看它是否是一个已经被参数化的types的实例。

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

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

这是错误信息:

不能对types参数E执行instanceof检查,而是使用它的erasure Object,因为genericstypes信息将在运行时被删除

有什么更好的方法来做到这一点?

错误消息说明了这一切。 在运行时,types不见了,无法检查它。

你可以通过为你的对象制造一个工厂来捕捉它:

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

然后在对象的构造函数存储中input这样的数字,这样你的方法可以如下所示:

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

使用generics进行运行时types检查的两个选项:

选项1 – 损坏你的构造函数

假设您正在重写indexOf(…),并且只想检查性能types,以节省自己迭代整个集合的时间。

像这样做一个肮脏的构造函数:

 public MyCollection<T>(Class<T> t) { this.t = t; } 

然后你可以使用isAssignableFrom来检查types。

 public int indexOf(Object o) { if ( o != null && !t.isAssignableFrom(o.getClass()) ) return -1; //... 

每次你实例化你的对象,你都不得不重复自己:

 new MyCollection<Apples>(Apples.class); 

你可能会认为这是不值得的。 在ArrayList.indexOf(…)的实现中,他们不检查types是否匹配。

选项2 – 让它失败

如果你需要使用一个需要你未知types的抽象方法,那么你真正想要的就是让编译器停止关于instanceof的哭泣。 如果你有这样的方法:

 protected abstract void abstractMethod(T element); 

你可以像这样使用它:

 public int indexOf(Object o) { try { abstractMethod((T) o); } catch (ClassCastException e) { //... 

你正在将对象转换为T(你的generics),只是为了欺骗编译器。 您的转换在运行时不做任何事情 ,但是当您尝试将错误types的对象传递到抽象方法时 ,您仍然会遇到ClassCastException。

注1:如果你正在抽象方法中进行额外的未经检查的强制转换,你的ClassCastExceptions将在这里被捕获。 这可能是好的或坏的,所以想一想。

注2: 当你使用instanceof的时候你会得到一个免费的空检查 。 既然你不能使用它,你可能需要徒手检查null。

假如你的类用一个generics参数扩展了一个类,你也可以在运行时通过reflection来获得它,然后使用它进行比较

 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,并且caching,所以你不会在多次检查时得到任何reflection开销。 请注意,您可以通过ParameterizedType.getActualTypeArguments()方法的索引获取其他参数化types。

旧的post,但一个简单的方法来做通用instanceOf检查。

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

我有同样的问题,这里是我的解决scheme(非常谦虚,@george:这次编译和工作…)。

我的探头是在一个抽象的类实现Observer。 Observable方法更新(…)与Object类可以是任何types的对象。

我只想处理typesT的对象

解决方法是将类传递给构造函数,以便能够在运行时比较types。

 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; } } 

从技术上讲,你不应该这样做,这就是generics,所以你可以进行编译types检查:

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

但是如果你有一个类层次结构,@Override可能会成为一个问题。 否则请参阅易's的回答。

对象的运行时types是一个相对任意的过滤条件。 我build议你远离你的藏品。 这只是通过让你的集合委托给一个构造中传递的filter来实现的。

 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; } }} );