I developed some code in Eclipse, tested it successfully, pushed it to our Jenkins CI server, and got an email that Maven was choking with a Java compile error. I subsequently isolated the problem and created the following minimal example showing the issue:
import java.util.List;
import java.util.function.Function;
class MinimalTypeFailureExample {
public static void main(String[] args) {
List<String> originalList = null; // irrelevant
List<IntToByteFunction> resultList = transform(originalList,
outer -> inner -> doStuff(inner, outer));
System.out.println(resultList);
}
static <F, T> List<T> transform(List<F> originalList,
MyFunction<? super F, ? extends T> function) {
return null; // irrelevant
}
static Byte doStuff(Integer inner, String outer) {
return null; // irrelevant
}
}
@FunctionalInterface
interface MyFunction<F, T> extends Function<F, T> {
@Override
T apply(F input);
}
@FunctionalInterface
interface IntToByteFunction {
Byte applyIntToByte(Integer inner);
}
In Eclipse, this code compiles without error and appears to execute as intended. However, compiling with javac gives the following error:
MinimalTypeFailureExample.java:7: error: incompatible types: cannot infer type-variable(s) F,T
List<IntToByteFunction> resultList = transform(originalList, outer -> inner -> doStuff(inner, outer));
^
(argument mismatch; bad return type in lambda expression
T is not a functional interface)
where F,T are type-variables:
F extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
T extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
1 error
Changing the argument type of transform()
from MyFunction
to Function
, or removing the wildcard ? extends
in the argument type, makes the example code compile in javac.
Clearly, either Eclipse or javac is in violation of the Java Language Specification. The question is, do I file the bug report on Eclipse or javac? The type inference rules for generic lambdas are so complex that I have no idea whether this program is legal Java or not according to the JLS.
Motivation note
In the original code,
transform()
was Guava'scom.google.common.collect.Lists.transform()
. TheMyFunction
interface was Guava 'scom.google.common.base.Function
interface, which extendsjava.util.function.Function
for historical reasons.The purpose of this code was to create a view of a list of a first type as a list of a second type. The second type was a functional interface type and I wanted to populate the output list with functions of this type constructed based on the values in the input list—hence the curried lambda expression.
Version info for reproducibility
Eclipse versions tested:
- 2018-09 (4.9.0) Build id: 20180917-1800
- 2019-03 RC1 (4.11 RC1) Build id: 20190307-2044
javac versions tested:
- 1.8.0_121
- JDK 10.0.1 via the JDoodle online Java compiler