Java:Instanceof 和泛型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1570073/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 14:13:24  来源:igfitidea点击:

Java: Instanceof and Generics

javagenericsinstanceoftypechecking

提问by Nick Heiner

Before I look through my generic data structure for a value's index, I'd like to see if it is even an instance of the type thishas been parametrized to.

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

But Eclipse complains when I do this:

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

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

This is the error message:

这是错误消息:

Cannot perform instanceof check against type parameter E. Use instead its erasure Object since generic type information will be erased at runtime

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

What is the better way to do it?

什么是更好的方法?

采纳答案by Yishai

The error message says it all. At runtime, the type is gone, there is no way to check for it.

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

You could catch it by making a factory for your object like this:

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

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

And then in the object's constructor store that type, so variable so that your method could look like this:

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

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

回答by Jason S

Technically you shouldn't have to, that's the point of generics, so you can do compile-type checking:

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

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

but then the @Override may be a problem if you have a class hierarchy. Otherwise see Yishai's answer.

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

回答by Tom Hawtin - tackline

The runtime type of the object is a relatively arbitrary condition to filter on. I suggest keeping such muckiness away from your collection. This is simply achieved by having your collection delegate to a filter passed in a construction.

对象的运行时类型是一个相对任意的过滤条件。我建议不要让你的收藏中出现这种肮脏的东西。这可以通过将您的集合委托传递给在构造中传递的过滤器来实现。

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

回答by terryscotttaylor

Provided your class extends a class with a generic parameter, you can also get this at runtime via reflection, and then use that for comparison, i.e.

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

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

In the case above, at runtime you will get String.class from getParameterizedClass(), and it caches so you don't get any reflection overhead upon multiple checks. Note that you can get the other parameterized types by index from the ParameterizedType.getActualTypeArguments() method.

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

回答by ben

Or you could catch a failed attempt to cast into E eg.

或者您可以捕获失败的尝试转换为 E例如。

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

回答by SharkAlley

Two options for runtime type checking with generics:

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

Option 1 - Corrupt your constructor

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

Let's assume you are overriding indexOf(...), and you want to check the type just for performance, to save yourself iterating the entire collection.

假设您要覆盖 indexOf(...),并且您只想检查类型以提高性能,以节省迭代整个集合的时间。

Make a filthy constructor like this:

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

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

    this.t = t;
}

Then you can use isAssignableFromto check the type.

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

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

Each time you instantiate your object you would have to repeat yourself:

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

new MyCollection<Apples>(Apples.class);

You might decide it isn't worth it. In the implementation of ArrayList.indexOf(...), they do not check that the type matches.

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

Option 2 - Let it fail

选项 2 - 让它失败

If you need to use an abstract method that requires your unknown type, then all you really want is for the compiler to stop crying about instanceof. If you have a method like this:

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

protected abstract void abstractMethod(T element);

You can use it like this:

你可以这样使用它:

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

You are casting the object to T (your generic type), just to fool the compiler. Your cast does nothing at runtime, but you will still get a ClassCastException when you try to pass the wrong type of object into your abstract method.

您将对象强制转换为 T(您的泛型类型),只是为了欺骗编译器。您的演员表在运行时什么都不做,但是当您尝试将错误类型的对象传递给抽象方法,您仍然会得到 ClassCastException。

NOTE 1: If you are doing additional unchecked casts in your abstract method, your ClassCastExceptions will get caught here. That could be good or bad, so think it through.

注意 1:如果您在抽象方法中进行额外的未经检查的强制转换,您的 ClassCastExceptions 将在此处被捕获。这可能是好是坏,所以请三思。

NOTE 2: You get a free null check when you use instanceof. Since you can't use it, you may need to check for null with your bare hands.

注意 2:当您使用 instanceof 时,您将获得免费的空检查。由于您无法使用它,因此您可能需要徒手检查是否为 null。

回答by hsaturn

I had the same problem and here is my solution (very humble, @george: this time compiling AND working ...).

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

My probem was inside an abstract class that implements Observer. The Observable fires method update(...) with Object class that can be any kind of Object.

我的probem 在一个实现Observer 的抽象类中。Observable 使用可以是任何类型的 Object 类的 Object 类触发方法 update(...)。

I only want to handler Objects of type T

我只想处理 T 类型的对象

The solution is to pass the class to the constructor in order to be able to compare types at runtime.

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

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

}

For the implementation we just have to pass the Class to the constructor

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

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

  @Override
  public void foo(Rule t){
  }
}

回答by Jonas Pedersen

Old post, but a simple way to do generic instanceOf checking.

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

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