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