【问题标题】:Why "t instanceof T" is not allowed where T is a type parameter and t is a variable?为什么在 T 是类型参数且 t 是变量的情况下不允许使用“t instanceof T”?
【发布时间】:2012-02-03 05:55:26
【问题描述】:

Eclipse 表示由于泛型类型橡皮擦,Type Parameter 不允许使用 instanceof 操作。

我同意在运行时不会保留任何类型信息。但请考虑以下类的通用声明:

class SomeClass<T>{
    T t;
    SomeClass(Object o){
        System.out.println(o instanceof T);   // Illegal
    }   
}        

在运行时,不会出现 T!但是如果我实例化这个Integer类型的类,那么对应的对象就会有一个Integer类型的字段t。

那么,为什么我不能用 T 检查变量的类型,它可以在运行时被 Integer 替换。我实际上会做类似“o instanceof Integer”之类的事情。

在哪些情况下,允许带有类型参数的instanceof会引起麻烦,从而被禁止?

【问题讨论】:

  • 您已经说过“在运行时,不会出现 T”,所以听起来您已经意识到 类型擦除。所以我不确定你为什么对这种行为感到困惑?
  • 不被允许不代表会造成卢布。 T 是一个泛型类型,实际上可以是任何东西,使用它 instanceof 没有任何意义。

标签: java generics instanceof type-parameter


【解决方案1】:

但是如果我实例化这个类型的 Integer 类,那么 对应的对象将有一个整数类型的字段 t

不,不会。它将有一个 Object 类型的字段。只是每次访问它时,它都会被转换为整数。

考虑以下代码:

SomeClass<Integer> c = new SomeClass<Integer>();
SomeClass untyped = (SomeClass)c; // Which type was it?
SomeClass<String> stringTyped = (SomeClass<String>)untyped; // Now it's STRING??

有效。给你一堆编译器警告,但有效。因为字段 T 实际上是 Object 类型,并且可以转换为任何类型。

【讨论】:

    【解决方案2】:

    如果你在运行时需要 T,你需要在运行时提供它。这通常通过传递 T 必须是的 Class 来完成。

    class SomeClass<T> {
        final T t;
    
        public SomeClass(Class<T> tClass, T t) {
            if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
            this.t = t;
        }
    
        private SomeClass(T t) {
            this.t = t;
        }
    
        public static <T> SomeClass<T> of(Class<T> tClass, T t) {
            if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
            return new SomeClass(t);
        }
    } 
    
    // doesn't compile
    SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, "one");
    
    Class clazz = Integer.class;
    // compiles with a warning and throws an IAE at runtime.
    SomeClass<Integer> intSomeClass = (SomeClass<Integer>) SomeClass.of(clazz, "one");
    
    // compiles and runs ok.
    SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, 1);
    

    【讨论】:

      【解决方案3】:

      编译语句后o instanceof T 将是o instanceof Object 并且因为所有类型都派生自Object,所以它总是评估为真。允许这种测试会产生假阳性结果

      【讨论】:

        【解决方案4】:

        因为type erasure,这永远行不通。在运行时,您只知道您的类有一个类型参数T,但不知道它是给定实例的类型。所以你一开始就无法确定一个对象是否属于T类型,因为你不知道T是什么,而不是因为它会引起某种麻烦。

        如果您需要进行此类运行时检查,请将类型标记显式传递给您的对象:

        SomeClass(Object o, Class<T> type) {
            System.out.println(type.isInstance(o));
        }
        

        【讨论】:

          【解决方案5】:

          但是如果我实例化这个Integer类型的类,那么对应的对象就会有一个Integer类型的字段t。

          实际上,它不会。它会有一个t 类型为Object 的字段。正如您所说,泛型几乎完全是语法糖(例外是当您扩展泛型类并指定类型参数时,类型仍作为元数据保留在类文件中)。

          【讨论】:

            【解决方案6】:

            泛型类型参数在运行时是未知的,因此没有可以与之比较的类。 T 仅在编译时已知。泛型只会帮助开发人员更轻松地编写代码。但在运行时,参数只是 Object 实例。

            【讨论】:

              【解决方案7】:

              Java使用“Erasure”实现其泛型,可以检查类型并删除“COMPILE TIME”中的类型参数信息,在“RUN TIME”中会出现只有类型参数的BOUNDARIES,所以不会有“Integer”之类的东西

              【讨论】:

                猜你喜欢
                • 2016-12-01
                • 2011-02-24
                • 2010-11-01
                • 2015-04-24
                • 2014-10-28
                • 2020-09-19
                • 2011-05-06
                相关资源
                最近更新 更多