Java:Instanceof 和泛型

发布于 2024-08-08 00:41:06 字数 341 浏览 2 评论 0原文

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

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

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

这是错误消息:

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

更好的方法是什么?

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 this has been parametrized to.

But Eclipse complains when I do this:

@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

What is the better way to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

债姬 2024-08-15 00:41:06

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

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

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

然后在对象的构造函数中存储该类型,因此变量,以便您的方法可以如下所示:

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

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;
}
柏拉图鍀咏恒 2024-08-15 00:41:06

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

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

您可能会认为它不值得。在ArrayList.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。

Two options for runtime type checking with generics:

Option 1 - Corrupt your constructor

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

Make a filthy constructor like this:

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

    this.t = t;
}

Then you can use isAssignableFrom to check the type.

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.

Option 2 - Let it fail

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:

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.

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.

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.

北城挽邺 2024-08-15 00:41:06

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

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

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

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
英雄似剑 2024-08-15 00:41:06

如果你的类扩展了一个带有泛型参数的类,你也可以在运行时通过反射获取它,然后使用它进行比较,即

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

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.

舂唻埖巳落 2024-08-15 00:41:06

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

我的探针位于一个实现 Observer 的抽象类中。
Observable 使用可以是任何类型的对象的对象类触发 update(...) 方法。

我只想处理 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){
  }
}

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

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.

I only want to handler Objects of type 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

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

  @Override
  public void foo(Rule t){
  }
}
猫九 2024-08-15 00:41:06

或者您可能会发现尝试转换为 E 例如
失败。

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

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

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
随波逐流 2024-08-15 00:41:06

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

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

但是如果您有类层次结构,则 @Override 可能会成为问题。否则请参阅 Yishai 的回答。

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.

剑心龙吟 2024-08-15 00:41:06

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

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

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;
             }
         }}
     );
旧街凉风 2024-08-15 00:41:06

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

Let Java determine it and catch the exception bottom line.

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
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文