Open Madjosz opened 9 months ago
@stephan-herrmann - may we hear from the master of type inference please ?? 😊
A colleague was able to reduce the example even more:
public class Generic {
public static void main(String... args) {
run(() -> supplyNull(new RuntimeException()));
runT(() -> supplyNull(new RuntimeException()));
}
static <R, X extends Exception> R supplyNull(X... excs) {
System.out.println(excs.getClass().getComponentType());
return null;
}
// behaviour differs when <T> is present
static void run(Runnable runnable) { runnable.run(); }
static <T> void runT(Runnable runnable) { runnable.run(); }
}
To clarify the behaviour I also opened a bug at Oracle/OpenJDK: https://bugs.openjdk.org/browse/JDK-8325859
A colleague was able to reduce the example even more:
Thanks. This looks great.
Here the lambda is being inferred against target type Runnable
in both cases. I see no indication what so ever how the (unused!) type parameter <T>
could possibly change inferred types for the lambda. True, that <T>
must now be inferred, and without any constraints I'm sure it will get inferred as j.l.Object
. But this doesn't change the Runnable
parameter.
To clarify the behaviour I also opened a bug at Oracle/OpenJDK: https://bugs.openjdk.org/browse/JDK-8325859
Cool, I'm curious to see if you'll get an answer there.
Update: See an even more reduced example below.
Consider the following example:
The
CheckedSupplier
should be generic over the type of the supplied valueT
and the type of the checked exceptionX
which can be thrown.supplyNullOnExc
should capture the actual type of the Exception at compile time via thereified
varargs array. For demonstration purposes I printed the type in the method.The above code resembles a minified version an actual JUnit5 test case. We obsereved different behaviours of the code compiled by ECJ vs. javac: ECJ (and javac 8) inferes
X
asIOException
(which is what we expect asread()
throwsIOException
) whilejavac
(starting at JDK 9) infers it just asException
.Removing the type parameter
<T>
fromassertThrows()
leads to javac being able to also inferIOException
so this might as well be a bug in javac but I am not well-versed in the JLS to determine which of the compilers actually follows the specs here.Tested versions
javac 1.8.0_402
(IOException
)javac 9.0.4
(Exception
)javac 21.0.2
(Exception
)javac 23-ea+8
(Exception
)ecj v20231114-0937, 3.36.0
(IOException
)ecj v20240203-1052, 3.37.0
(IOException
)ECJ builds with target
-21
, execution environment version did not matter