Consider the following two classes and interface:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
Why does the second call to mandatory
invoke the overloaded method with Class2
, if getInterface1
and Interface1
have no relationship with Class2
?
public class Test {
public static void main(String[] args) {
Class1 class1 = getClass1();
Interface1 interface1 = getInterface1();
mandatory(getClass1()); // prints "T is not class2"
mandatory(getInterface1()); // prints "T is class2"
mandatory(class1); // prints "T is not class2"
mandatory(interface1); // prints "T is not class2"
}
public static <T> void mandatory(T o) {
System.out.println("T is not class2");
}
public static <T extends Class2> void mandatory(T o) {
System.out.println("T is class2");
}
public static <T extends Class1> T getClass1() {
return null;
}
public static <T extends Interface1> T getInterface1() {
return null;
}
}
I understand that Java 8 broke compatibility with Java 7:
$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac -source 1.7 -target 1.7 *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
T is not class2
T is not class2
T is not class2
T is not class2
And with Java 8 (also tested with 11 and 13):
$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
T is not class2
T is class2
T is not class2
T is not class2
From the point of view of reflection, the difference between a generic type and an ordinary type is that a generic type has associated with it a set of type parameters (if it is a generic type definition) or type arguments (if it is a constructed type). A generic method differs from an ordinary method in the same way.
We use <T> to create a generic class, interface, and method. The T is replaced with the actual type when we use it.
Generic Interfaces in Java are the interfaces that deal with abstract data types. Interface help in the independent manipulation of java collections from representation details. They are used to achieving multiple inheritance in java forming hierarchies. They differ from the java class.
The rules of type inference have received a significant overhaul in Java 8, most notably target type inference has been much improved. So, whereas before Java 8 the method argument site did not receive any inference, defaulting to erased type (Class1
for getClass1()
and Interface1
for getInterface1()
), in Java 8 the most specific applicable type is inferred. The JLS for Java 8 introduced a new chapter Chapter 18. Type Inference that's missing in JLS for Java 7.
The most specific applicable type for <T extends Interface1>
is <X extends RequiredClass & BottomInterface>
, where RequiredClass
is a class required by a context, and BottomInterface
is a bottom type for all interfaces (including Interface1
).
Note: Each Java type can be represented as SomeClass & SomeInterfaces
. Since RequiredClass
is sub-type of SomeClass
, and BottomInterface
is sub-type of SomeInterfaces
, X
is sub-type of every Java type. Therefore, X
is a Java bottom type.
X
matches both public static <T> void mandatory(T o)
and public static <T extends Class2> void mandatory(T o)
methods signatures since X
is Java bottom type.
So, according to §15.12.2, mandatory(getInterface1())
calls the most specific overloading of mandatory()
method, which is public static <T extends Class2> void mandatory(T o)
since <T extends Class2>
is more specific than <T>
.
Here is how you can explicitly specify getInterface1()
type parameter to make it return the result that matches public static <T extends Class2> void mandatory(T o)
method signature:
public static <T extends Class2 & Interface1> void helper() {
mandatory(Test.<T>getInterface1()); // prints "T is class2"
}
The most specific applicable type for <T extends Class1>
is <Y extends Class1 & BottomInterface>
, where BottomInterface
is a bottom type for all interfaces.
Y
matches public static <T> void mandatory(T o)
method signature, but it doesn't match public static <T extends Class2> void mandatory(T o)
method signature since Y
doesn't extend Class2
.
So mandatory(getClass1())
calls public static <T> void mandatory(T o)
method.
Unlike with getInterface1()
, you can't explicitly specify getClass1()
type parameter to make it return the result that matches public static <T extends Class2> void mandatory(T o)
method signature:
java: interface expected here
↓
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
mandatory(Test.<T>getClass1());
}
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