Java: Instanceof 和泛型

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

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

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

下面是错误消息:

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

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

214200 次浏览

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

你可以像这样为你的对象建立一个工厂:

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

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

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

从技术上讲,你不必这么做,这就是泛型的意义所在,所以你可以做编译类型检查:

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

但如果你有一个类层次结构,那么@Override 可能是个问题。

对象的运行时类型是要筛选的相对任意的条件。我建议你不要把这些脏东西收藏起来。这只是通过将集合委托传递到构造中的筛选器来实现的。

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

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

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

在上面的例子中,在运行时,您将从 getParameter terizedClass ()获得 String.class,并且它会缓存,这样您就不会在多次检查时产生任何反射开销。注意,您可以通过参数化 Type.getAccounalTypeArguments ()方法的索引获得其他参数化类型。

或者你可能会发现一次失败的尝试,试图转换成 E < strong > eg. 。

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

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

选项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);

您可能认为这样做不值得。在 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 检查。由于你不能使用它,你可能需要用你的双手来检查 null。

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

我的问题在实现 Observer 的抽象类中。 可观察的触发方法更新(...)的 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){
}
}

旧文章,但是一个简单的方法来做泛型 instanceOf 检查。

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

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