Is there any rule for how type inference is made
Yes, the whole 18th chapter of the Java language specification is dedicated to this topic :-)
and why does he choose D?
I think the following rule is responsible for this:
If the connected set does not contain an estimate of the form G <..., αi, ...> = capture (G <...>) for all i (1 ≤ i ≤ n), then a candidate copy 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 associated 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 its own upper bounds U1, ..., Uk, Ti = glb (U1, ..., Uk) (§ 5.1.1).
The estimates α1 = T1, ..., αn = Tn are included in the current connected set.
If the result does not contain a bound false, then the result becomes a new bound set, and the resolution continues, choosing a new set of variables to create the instance (if necessary), as described above.
In plain English, when you check for possible values for a type parameter, the compiler first tries to use the lower bound and uses it if appropriate.
In our case, the set of constraints says that D extends T and D extends A , so the lower bound for T is D , so D is the first candidate replacement.
The compiler, which checks whether D is suitable, assuming that T = D , which simplifies this restriction set on D extends D and D extends A , both of which are known as true.
Therefore, the compiler uses D