【问题标题】:how to compare type of an object's instance to a generic type?如何将对象实例的类型与泛型类型进行比较?
【发布时间】:2011-05-17 11:14:49
【问题描述】:

如何用java编写这段代码?

public class ComponentsManager 
    {
        private List<IComponent> list = new ArrayList<IComponent>();

        public <U extends IComponent> U GetComponent() {

            for (IComponent component : list) {

                if(component instanceof U)
                {
                    return component;
                }
            }
        }
}

但我不能对泛型类型执行 instanceof。我该怎么做? 谢谢。

【问题讨论】:

    标签: java generics reflection


    【解决方案1】:

    由于类型擦除,基本上你不能这样做。正常的解决方法是传递一个Class 对象作为参数;例如

        public <U extends IComponent> U GetComponent(Class<U> clazz) {
            for (IComponent component : list) {
                if (clazz.isInstance(component)) {
                    return clazz.cast(component);
                }
            }
        }
    

    您也可以使用if (clazz.equals(component.getClass())) { ...,但这会进行精确的类型匹配......这不是instanceof 运算符所做的。 instanceof 运算符和 Class.instanceOf 方法都测试以查看值的类型是否赋值兼容

    【讨论】:

    • @Steven C:你愿意评论我的回答吗?每个人都说“不能”,但我的小演示有效..
    • 为什么 Oracle/Java 不增加泛型类型访问 .class 字段(/method/identifier)的可能性?我是否可以假设原因与编译时间与运行时发生的情况有关?
    • 根本原因是类型擦除。类型擦除的根本原因是泛型类型需要实现为编译时机制(在 Java 5 中)以与早期版本的 Java 兼容。
    • 参数的名称“clazz”让我很奇怪。但似乎 SO 上的几乎每个示例都使用完全相同的名称。
    • 尝试改用class,看看会发生什么。 (然后关掉你的“怪异传感器”。)
    【解决方案2】:

    您可以尝试添加几个“测试”方法:

    private static <U extends IComponent> boolean testComponent(U u) {
      return true;
    }
    
    private static boolean testComponent(Object o) {
      return false;
    }
    

    然后,使用testComponent(component) 而不是component instanceof U

    示例代码:

    import java.util.*;
    
    class IComponent {
    }
    
    class T1 extends IComponent {
    }
    
    public class Test {
    
      public static <U extends IComponent> boolean testComponent(U u) {
        return true;
      }
    
      public static boolean testComponent(Object o) {
        return false;
      }
    
      public static void main(String[] args) {
        T1 t = new T1();
        System.out.println("hm? " + (testComponent(t) ? "true" : "false"));
      }
    }
    

    输出:

    嗯?真的

    【讨论】:

    • (因为@sje397 要求我发表评论...):这将“有效”,但您必须在GetComponent 方法内进行不安全的类型转换...或将其更改为非通用方法。此外,您通过添加自己的 ersatz 类型检查来复制 Java 的类型检查。这很麻烦,而且最终的解决方案比使用真正的 Java 类型检查的解决方案更脆弱。
    • 即使考虑到 Stephen 的 cmets,这也是有用的。哦等等,这似乎没有编译“相同类型的擦除”。 @sje397:这对你有用吗?显然你必须在每个类中包含方法?那不是那么有用..
    • 这个“答案”所做的只是证明您可以将扩展 IComponent 的对象定向到特定方法。问题是,一旦您使用该方法,类型擦除意味着您不知道它是“U”,因此您无法基于“U”进行任何推理——这是解决问题所需要的题。在方法“testComponent”中所知道的只是“u”是一个 IComponent。更好的测试是传递 两个 参数“U u”和“IComponent other”,然后表明您可以确定“other”是否为“U”。但在这一点上,最好通过所需的类。
    • 或者换一种说法:当然它返回true这只是一个重载的演示;带有参数 IComponent 的非泛型方法将具有完全相同的行为。现在证明“u extends IComponent”会给你带来任何好处。我不认为是这样,但即使我错了,传递一个 Class 将是一种更清洁的方式来完成结果。
    【解决方案3】:

    java 的一般规则——如果一个泛型类需要知道它的泛型类型,那么你必须传递一个提示。解决该问题的常用方法是使用构造函数:

    public class ComponentsManager<U extends IComponent> {
      private Class<U extends IComponentManeger> genericType = null;
      public ComponentsManager<U extends IComponent>(
                           Class<U extends IComponent> genericType) {
        this.genericType = genericType;
      }
    
    }
    

    现在该类知道它是泛型类型类,您可以使用泛型类型类实例来验证您的集合中的组件是否与类泛型类型匹配。

    【讨论】:

      【解决方案4】:

      据我所知,你不能。您必须将 Class 对象作为参数:

      public <U extends IComponent> U getComponent(Class<U> clazz) {
          // ...
          if (component.getClass() == clazz) {
              return (U) component;
          }
      }
      

      然后这样称呼它:

      getComponent(MyComponentImpl.class);
      

      【讨论】:

      • 你是对的@斯蒂芬。我忘了投射结果。感谢您指出。
      • 也不会编译。试试看。
      • @Stephen:我试过了,它可以工作(至少在使用 JDK 6 的 Eclipse 中)。它编译时带有“未经检查的演员表”警告,但它工作正常。你得到什么错误?
      • 这就是我的意思。大多数人认为这些是诅咒。问题是,虽然它在实践中是安全的,但一个小的变化就会导致它不安全。因此,忽略或压制它是一个坏主意。相反,您应该将其视为硬编译错误并修复它。
      【解决方案5】:

      确切的泛型类型不能被视为代码中的普通变量,这使得直接进行比较变得困难。但是反射类ParameterizedType可以帮助你获得一个表示参数化类型的实例,它基本上是实用的对象。

      Type genericSuperType = object.getClass().getGenericSuperclass()
      Type[] actualTypeParams = ((ParameterizedType) genericSuperType).getActualTypeArguments()
      if (actualTypeParams[0] instanceof Class) {
          ((Class) actualTypeParams[0]).isInstance(testingObj);
      }
      

      更多关于ParameterizedType和对应TypeVariable的使用细节,可以参考内部util类TypeParameterMatcher`, in Netty

      【讨论】:

        猜你喜欢
        • 2012-05-11
        • 2015-01-31
        • 1970-01-01
        • 2016-06-14
        • 1970-01-01
        • 2010-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多