I have a piece of code which compiles fine under java 7, but not under java 8. Here is a self-contained reproducing example (I've taken real code that exhibits this problem and stubbed out all the implementations):
import java.util.Iterator;
class ASTNode<T extends ASTNode> implements Iterable<T> {
@Override public Iterator<T> iterator() { return null; }
}
class List<T extends ASTNode> extends ASTNode<T> {}
interface Function<F, T> {}
class Iterables {
public static <F,T> Iterable<T> transform(
Iterable<F> fromIterable, Function<? super F, ? extends T> function) { return null; }
}
class AstFunctions {
public static <T extends ASTNode<?>> Function<T, String> prettyPrint() { return null; }
}
public class Main {
public static void test() {
List<? extends ASTNode<?>> list = null;
Iterables.transform(list, AstFunctions.prettyPrint());
}
}
Witness:
$ javac -version
javac 1.8.0_05
$ javac -source 1.7 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
$ javac -source 1.8 Main.java
Main.java:23: error: method transform in class Iterables cannot be applied to given types;
Iterables.transform(list, AstFunctions.prettyPrint());
^
required: Iterable<F>,Function<? super F,? extends T#1>
found: List<CAP#1>,Function<ASTNode<?>,String>
reason: cannot infer type-variable(s) F,T#1,T#2
(argument mismatch; Function<CAP#1,String> cannot be converted to Function<? super CAP#1,? extends String>)
where F,T#1,T#2 are type-variables:
F extends Object declared in method <F,T#1>transform(Iterable<F>,Function<? super F,? extends T#1>)
T#1 extends Object declared in method <F,T#1>transform(Iterable<F>,Function<? super F,? extends T#1>)
T#2 extends ASTNode<?> declared in method <T#2>prettyPrint()
where CAP#1 is a fresh type-variable:
CAP#1 extends ASTNode<?> from capture of ? extends ASTNode<?>
1 error
(Perhaps notable is that Eclipse, configured for 1.8 compatibility, has no issues with this code).
Is this a compiler bug? If not, then assuming I'm allowed to change AstFunctions
and Main
(but not ASTNode
, List
, Function
, or Iterables
), how can I make this code compile? I'd also like to understand, if possible, what change to Java 8's type system makes this code not compile.
This is not an issue in current version of the compiler at last javac 9. Any apparent bug should be checked against this version before any other consideration.
For last javac8 code the repository to check is last javac 8. This issue has already been fixed in that repository. The issue has been fixed with the patch for JDK-8033718
UPDATE: see other answer - this was a bug in javac which has been fixed.
It feels like this should not compile and that Java 8 is exhibiting the correct behaviour:
Iterables.transform
expects an Iterable<F> fromIterable
and Function<? super F...
, so the first generic type of Function
needs to be a super class of the generic type of the Iterable
F1 == ? extends ASTNode<?>
and the first type of the Function returned by prettyPrint
is F2 == T extends ASTNode<?>
I don't think there is a way to prove that F2 is a supertype of F1. For example let's say you have:
class A1 extends ASTNode<A1> {}
class A2 extends ASTNode<A2> {}
and in your main:
List<? extends ASTNode<?>> list = new List<A1>(); //F1 = A1
You could imagine that prettyPrint
returns a Function<A2, String>
(i.e. F2 = A2
) and A2 is not a super class of A1.
So I believe the compile error makes sense. But I won't try to prove it based on the specifications because that would eat most of my day!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With