Java:Instanceof和Generics

Nic*_*ner 127 java generics instanceof typechecking

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

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

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }
Run Code Online (Sandbox Code Playgroud)

这是错误消息:

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

有什么更好的方法呢?

Yis*_*hai 76

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

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

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }
Run Code Online (Sandbox Code Playgroud)

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

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
Run Code Online (Sandbox Code Playgroud)

  • @luis,Tom的评论可能意味着我应该使用isInstance()并传递实际的arg0参数.它具有避免空检查的优点. (6认同)
  • 我不认为你想要`Class.isAssignableFrom`. (3认同)

Sha*_*ley 38

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

选项1 - 破坏您的构造函数

让我们假设您重写indexOf(...),并且您想要检查类型的性能,以节省自己迭代整个集合.

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

public MyCollection<T>(Class<T> t) {

    this.t = t;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用isAssignableFrom检查类型.

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...
Run Code Online (Sandbox Code Playgroud)

每次实例化对象时,都必须重复自己:

new MyCollection<Apples>(Apples.class);
Run Code Online (Sandbox Code Playgroud)

你可能认为它不值得.在ArrayList.indexOf(...)的实现中,它们不检查类型是否匹配.

选项2 - 让它失败

如果您需要使用需要未知类型的抽象方法,那么您真正想要的就是让编译器停止对instanceof的哭泣.如果您有这样的方法:

protected abstract void abstractMethod(T element);
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...
Run Code Online (Sandbox Code Playgroud)

您正在将对象转换为T(您的泛型类型),只是为了欺骗编译器.您的转换在运行时不执行任何操作,但是当您尝试将错误类型的对象传递到抽象方法,仍会得到ClassCastException.

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

注意2:使用instanceof时,您将获得免费的空值检查.由于您无法使用它,您可能需要赤手检查null.


Jon*_*sen 14

旧帖子,但是通用instanceOf检查的简单方法.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
Run Code Online (Sandbox Code Playgroud)

  • 目前还不清楚你在这里做出了什么贡献. (18认同)

小智 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,在运行时,您将从getParameterizedClass()获取String.class,并且它会缓存,因此您不会在多次检查时获得任何反射开销.请注意,您可以通过ParameterizedType.getActualTypeArguments()方法的索引获取其他参数化类型.


hsa*_*urn 7

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

我的probem在一个实现Observer的抽象类中.Observable使用Object类触发方法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);

}
Run Code Online (Sandbox Code Playgroud)

对于实现,我们只需将Class传递给构造函数

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

或者你可以抓住一个失败的尝试投射到E 例如.

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

  • 这不行.E在运行时被删除,因此强制转换不会失败,您只会收到编译器警告. (23认同)