In this code, T can be A, B, C, or D, but Eclipse shows that it is D.
static class A { }
static class B extends A { }
static class C extends B { }
static class D extends C { }
static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (T t : src)
dst.add(t);
}
public static void main(String[] args) {
List<A> dst = new ArrayList<>();
List<D> src = new ArrayList<>();
copy(dst, src); // Eclipse shows T is D
}
Is there any rule for how type inference is done and why it selects D?
Is there any rule for how type inference is done
Yes, the entire 18th chapter of the Java Language Specification is dedicated to this topic :-)
and why it selects D?
I think the following rule is responsible for that:
If the bound set does not contain a bound of the form G<..., αi, ...> = capture(G<...>) for all i (1 ≤ i ≤ n), then a candidate instantiation Ti is defined for each αi:
If αi has one or more proper lower bounds, L1, ..., Lk, then Ti = lub(L1, ..., Lk) (§4.10.4).
Otherwise, if the bound set contains throws αi, and the proper upper bounds of αi are, at most, Exception, Throwable, and Object, then Ti = RuntimeException.
Otherwise, where αi has proper upper bounds U1, ..., Uk, Ti = glb(U1, ..., Uk) (§5.1.10).
The bounds α1 = T1, ..., αn = Tn are incorporated with the current bound set.
If the result does not contain the bound false, then the result becomes the new bound set, and resolution proceeds by selecting a new set of variables to instantiate (if necessary), as described above.
In plain english, when trying out possible values for a type parameter, the compiler first tries the lower bound, and uses that one if it fits.
In our case, the constraint set says that D extends T and D extends A
, so the lower bound for T
is D
, so D
is the first candidate substitution.
The compiler than verifies whether D
fits by assuming that T = D
, which simplifies that constraint set to D extends D and D extends A
, both of which are known to be true.
Therefore, the compiler uses D
.