简体   繁体   中英

(Generics)Cannot make a static reference to the non-static type T

running the Demo class will invoke a static method newInstance in SomeClass to call the constructor and printing hello

defining a method will include a return type + method name with arguments

the return type for newInstance is <T>SomeClass<T> seems weird to me since my class is called SomeClass<T> instead of <T>SomeClass<T>

why do i need the <T> in front of the SomeClass<T> ? it seems that if I don't include it there will be an common error called Cannot make a static reference to the non-static type T

another thing to point out is that I can put many spaces between <T> and SomeClass<T> so it doesn't seem like they need to be together.

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

What is a static method? A Method that works on the class, and not a specific instance. The generic parameter T in the class signature public class SomeClass<T> is only available for a specific instance (hence non-static type T ). eg SomeClass<String> where the [T = String] .

By including <T> in the method signature of public static <T>SomeClass<T> newInstance(Class<T> clazz) . You're saying that; for this method, there is a generic type argument T . This T is separate from the T in the class signature. So it might as well be C ie public static <C> SomeClass<C> newInstance(Class<C> clazz) . Or something completely different.

But if you don't include <T> with the method, the compiler thinks you're trying to use the T in the class signature. Which is illegal.

In my opinion, the question is very interesting, it addresses several distinct issues and deserves more than one answer, although that one might be perfectly correct.

why do i need the in front of the SomeClass [Otherwise] there will be an ... error ... Cannot make a static reference to the non-static type T

The usage of static members, methods and fields, in a generic class has certain restrictions , see a section "Cannot Declare Static Fields Whose Types are Type Parameters". Although the section discusses fields, not methods, one could extend the reasoning to the methods as well. Indeed, if we agree that the declaration

 private static T foo = null;

makes no sense because the compiler couldn't create an instance-independent variable foo as the generic class could be parameterized with any type (Remember that Java Generic is not C++ Template, and due to the type erasure of the former there is one and only one (per a class loader) instance of a generic class in a runtime), then why something like

public static T get() {
    return null;
}

should make more sense?

Second, when you declare generic type T in your SomeClass and then in newInstance method, you in fact hide the first T with the second one, and both are unrelated to each other. The type hiding is more obvious for non-static types. In the example

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

the second T , declared in a method, hides the first one, declared for the class, and that's where you receive a warning The type parameter T is hiding the type T . To eliminate the warning, you have to replace T in the method with another type variable, eg Z , so that distinction between the two becomes obvious.

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

In a case of static method, the compiler does not issue a warning (probably because it thinks that for static things the hiding is assumed?), but the type hiding exists in this case as well. In the example you brought you managed to successfully cheat the Java Generic machinery (congrats :) ), but if we imagine the following scenario (I slightly refactored the semantic of your example to, let say, more traditional one)

static class Factory <T extends Number> { 
    private T t;

    public static <T> T getInstance(){
        return null;
    }

    public T get(){
        return t;
    }
}

, the both lines will compile

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

, but you lose type safety on the second, so that the line

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

fails, which is perfectly fine, but the line

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

which is not fine and is not what you'd expect if you miss the point of type parameter hiding.

(Under the hood, T longFactory.get() type-erases to Number longFactory.get() , while static <T> T getInstance() type-erases to static Object getInstance() , eg

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

but Type Erasure is another story)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM