【问题标题】:(Generics)Cannot make a static reference to the non-static type T(泛型)无法对非静态类型 T 进行静态引用
【发布时间】:2021-05-11 04:29:25
【问题描述】:

运行Demo类会调用SomeClass中的静态方法newInstance来调用构造函数并打印hello

定义方法将包括返回类型 + 方法名称和参数

newInstance 的返回类型是 SomeClass 对我来说似乎很奇怪 因为我的班级被称为 SomeClass 而不是 SomeClass

为什么我需要在 SomeClass 前面的 ? 似乎如果我不包括它会有一个常见的错误 调用不能对非静态类型T进行静态引用

要指出的另一件事是,我可以在 和 SomeClass 之间放置很多空格,因此它们看起来不需要放在一起。

public class SomeClass<T> {

    public static <T>SomeClass<T> newInstance(Class<T> clazz){
        return new SomeClass<T>(clazz);
    }

    private SomeClass(Class<T> clazz){
        System.out.println("hello");
    }
}

public class Demo {

    public static void main(String args[])
    {
        SomeClass<String> instance = SomeClass.newInstance(String.class);
    }
}

【问题讨论】:

标签: java generics


【解决方案1】:

什么是静态方法?一种适用于类的方法,而不是特定的实例。类签名public class SomeClass&lt;T&gt; 中的通用参数T 仅可用于特定实例(因此non-static type T)。例如SomeClass&lt;String&gt; 其中[T = String]。

通过在public static &lt;T&gt;SomeClass&lt;T&gt; newInstance(Class&lt;T&gt; clazz) 的方法签名中包含&lt;T&gt;。你这么说;对于这个方法,有一个泛型类型参数T。这个T 与类签名中的T 是分开的。所以它也可能是C,即public static &lt;C&gt; SomeClass&lt;C&gt; newInstance(Class&lt;C&gt; clazz)。或者完全不同的东西。

但是如果你没有在方法中包含&lt;T&gt;,编译器会认为你试图在类签名中使用T。这是非法的。

【讨论】:

    【解决方案2】:

    在我看来,这个问题非常有趣,它解决了几个不同的问题,值得得到不止一个答案,尽管那个答案可能完全正确。

    为什么我需要在SomeClass前面[否则]会有一个...错误...不能对非静态类型T进行静态引用

    在泛型类has certain restrictions 中静态成员、方法和字段的用法,请参见“不能声明类型为类型参数的静态字段”一节。尽管该部分讨论的是字段,而不是方法,但也可以将推理扩展到方法。事实上,如果我们同意声明

     private static T foo = null;
    

    没有意义,因为编译器无法创建与实例无关的变量foo,因为泛型类可以使用任何类型进行参数化(请记住,Java Generic 不是 C++ 模板,并且由于前者的类型擦除在运行时中只有一个(每个类加载器)泛型类的实例),那么为什么会这样

    public static T get() {
        return null;
    }
    

    应该更有意义吗?

    其次,当您在 SomeClass 和 newInstance 方法中声明泛型类型 T 时,实际上您 隐藏第一个 T 和第二个 T,两者都是彼此无关。对于非静态类型,类型隐藏更为明显。在示例中

    class Foo<T> {
        <T> T get(){ // a warning "The type parameter T is hiding the type T" 
            return null;
        }
    }
    

    第二个T,在方法中声明,隐藏第一个,为类声明,这就是你收到警告The type parameter T is hiding the type T的地方。要消除警告,您必须将方法中的 T 替换为另一个类型变量,例如Z,这样两者的区别就很明显了。

    class Foo<T> {
        <Z> Z get(){ // no warning, T and Z are different type variables
            return null;
        }
    }
    

    在静态方法的情况下,编译器不会发出警告(可能是因为它认为对于静态事物假设隐藏?),但在这种情况下也存在类型隐藏。在您提供的示例中,您成功地欺骗了 Java Generic 机器(恭喜:)),但是如果我们想象以下场景(我将您示例的语义稍微重构为更传统的语义)

    static class Factory <T extends Number> { 
        private T t;
    
        public static <T> T getInstance(){
            return null;
        }
    
        public T get(){
            return t;
        }
    }
    

    ,这两行都会编译

    Number n1 = longFactory.get();
    Number n2 = Factory.getInstance();
    

    ,但是你在第二个失去了类型安全,所以该行

    String s1 = longFactory.get(); // fails to compile with "Type mismatch: cannot convert from Long to String" error message 
    

    失败了,这很好,但是这条线

    String s2 = longFactory.getInstance(); // compiles only with a warning "static method should be accessed in a static way"
    

    这是不好的,如果你错过了类型参数隐藏的点,这不是你所期望的。

    (在后台,T longFactory.get() type-erases 到 Number longFactory.get(),而 static &lt;T&gt; T getInstance() type-erases 到 static Object getInstance(),例如

    System.out.println(Factory.class.getMethod("getInstance").getReturnType().getSimpleName());  // prints "Object"
    System.out.println(Factory.class.getMethod("get").getReturnType().getSimpleName()); // prints "Number"
    

    但Type Erasure 是另一回事)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-15
      • 2015-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      相关资源
      最近更新 更多