This piece of code compiles in Eclipse but not in javac:
import java.util.function.Consumer;
public class Test {
public static final void m1(Consumer<?> c) {
m2(c);
}
private static final <T> void m2(Consumer<? super T> c) {
}
}
javac output:
C:\Users\lukas\workspace>javac -version
javac 1.8.0_92
C:\Users\lukas\workspace>javac Test.java
Test.java:5: error: method m2 in class Test cannot be applied to given types;
m2(c);
^
required: Consumer<? super T>
found: Consumer<CAP#1>
reason: cannot infer type-variable(s) T
(argument mismatch; Consumer<CAP#1> cannot be converted to Consumer<? super T>)
where T is a type-variable:
T extends Object declared in method <T>m2(Consumer<? super T>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
1 error
----------------------------------------------------------------
Which compiler is wrong and why? (Eclipse bug report and parts of discussion here)
This code is legal wrt JLS 8. javac version 8 and earlier had several bugs in how they handle wildcards and captures. Starting with version 9 (early access, I tried version ea-113 and newer) also javac accepts this code.
To understand how a compiler analyzes this according to JLS, it is essential to tell apart what are wildcard captures, type variables, inference variables and such.
The type of c
is Consumer<capture#1-of ?>
(javac would write Consumer<CAP#1>
). This type is unknown, but fixed.
The parameter of m2
has type Consumer<? super T>
, where T
is a type variable to be instantiated by type inference.
During type inference an inference variable, denoted by ecj as T#0
, is used to represent T
.
Type inference consists in determining, whether T#0
can be instantiated to any type without violating any given type constraints. In this particular case we start with this contraint:
⟨c → Consumer<? super T#0>⟩
Which is stepwise reduced (by applying JLS 18.2):
⟨Consumer<capture#1-of ?> → Consumer<? super T#0>⟩
⟨capture#1-of ? <= ? super T#0⟩
⟨T#0 <: capture#1-of ?⟩
T#0 <: capture#1-of ?
The last line is a "type bound" and reduction is done. Since no further constraints are involved, resolution trivially instantiates T#0
to the type capture#1-of ?
.
By these steps, type inference has proven that m2
is applicable for this particular invocation. qed.
Intuitively, the shown solution tells us: whatever type the capture may represent, if T
is set to represent the exact same type, no type constraints are violated. This is possible, because the capture is fixed before starting type inference.