Can you explain why the following works?
public class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public static <T> void doSomethingStatic(T v1, T v2) {
}
public static <T> void doSomethingStaticList(List<T> v1, List<T> v2)
{
}
public static void main(String[] args) {
GenericsTest<String> gt = new GenericsTest<>();
// OK
gt.doSomething("abc", "abc");
// Not OK
gt.doSomething(1, "abc");
// OK
doSomethingStatic(1, 2);
// Still OK
doSomethingStatic(1, "abc");
// So why is this not OK?
List<String> list1=new LinkedList<>();
List<Integer> list2=new LinkedList<>();
doSomethingStaticList(list1,list2);
}
}
T v1, T v2
should be the same type in doSomethingStatic
, but I'm still able to pass different types(integer and string).
If doSomethingStatic()
takes a common super class by default, why doesn't doSomethingStaticList()
work with different types?
This is because, the static method can be called without even instantiating the Class. So if the Class is not yet instantiated, we do not yet know what is T. This is the reason why the static methods needs to have its own generics.
Using generics, type parameters are not allowed to be static. As static variable is shared among object so compiler can not determine which type to used. Consider the following example if static type parameters were allowed.
A static method in Java is a method that is part of a class rather than an instance of that class. Every instance of a class has access to the method. Static methods have access to class variables (static variables) without using the class's object (instance). Only static data may be accessed by a static method.
In non-static case you define T
as String
when you create instance of GenericsTest
. Hence passing an int
will give compile error. If you did gt.doSomething(1, 2)
it would fail as well.
In static case, you don't define T
manually, it is derived from parameters. It will be the first common superclass of both classes - which in this case is Object
. You might want to use bounded wildcard, e.g. <T extends Number>
or <T extends CharSequence>
.
Note that you have two different T
s here:
GenericsTest<T>
public static <T> void doSomethingStatic(T v1, T v2)
The declaration of generic parameter is whenever you write <T>
. You can use different letters in this case to avoid confusion.
This works because T
in your static method is his own type parameter, not the T
parameter for instance that used in your member method. Rename it to clarify:
public static class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public static <V> void doSomethingStatic(V v1, V v2) {
}
//...
So in case of doSomething(...)
your instance type parameter value is String
so it's an error. In case of static doSomethingStatic(...)
value of type parameter is different:
GenericsTest.doSomethingStatic(1, "abc"); //ok
GenericsTest.<Object>doSomethingStatic(1, "abc"); //ok
GenericsTest.<String>doSomethingStatic(1, "abc"); //not ok
new GenericsTest<String>().doSomething(1, "abc"); //not ok
First a bit of theory:
So what happen:
You can try also this example without static method:
public class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public <T> void doSomething2(T v1, T v2) {
}
public static void main(String[] args) {
GenericsTest<String> gt = new GenericsTest<>();
// ok
gt.doSomething("abc", "abc");
// Not ok
gt.doSomething(1, "abc");
// ok
gt.doSomething2(1, 2);
// Still ok
gt.doSomething2(1, "abc");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With