Consider the following example:
public class Learn {
public static <T> T test (T a, T b) {
System.out.println(a.getClass().getSimpleName());
System.out.println(b.getClass().getSimpleName());
b = a;
return a;
}
public static void main (String[] args) {
test("", new ArrayList<Integer>());
}
}
In the main
method, I am calling test
with a String
and an ArrayList <Integer>
object. Both are different things and assigning an ArrayList
to String
(generally) gives a compile error.
String aString = new ArrayList <Integer> (); // won't compile
But I am doing exactly that in the 3rd line of test
and the program compiles and runs fine. First I thought that the type parameter T
is replaced by a type that's compatible with both String
and ArrayList
(like Serializable
). But the two println
statements inside test
print "String" and "ArrayList" as types of a
and b
respectively. My question is, if a
is String
and b
is ArrayList
at runtime, how can we assign a
to b
.
For a generic method, the Java compiler will infer the most specific common type for both parameters a
and b
.
The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.
You aren't assigning the result of the call to test
to anything, so there is no target to influence the inference.
In this case, even String
and ArrayList<Integer>
have a common supertype, Serializable
, so T
is inferred as Serializable
, and you can always assign one variable of the same type to another. For other examples, you may even find Object
as the common supertype.
But just because you have variables of type T
that are inferred as Serializable
, the objects themselves are still a String
and an ArrayList
, so getting their classes and printing their names still prints String
and ArrayList
. You're not printing the type of the variables; you're printing the type of the objects.