Reputation: 502
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);
}
}
Upvotes: 11
Views: 25290
Reputation: 11
user provides a generic type at runtime, so a class cannot understand what is generic.
Upvotes: 0
Reputation: 2368
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, e.g. 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()
, e.g.
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)
Upvotes: 1
Reputation: 33895
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
). e.g. 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
i.e. 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.
Upvotes: 19