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 implements Iterable {
@Override public Iterator iterator() { return null; }
}
class List extends ASTNode {}
interface Function {}
class Iterables {
public static Iterable transform(
Iterable fromIterable, Function super F, ? extends T> function) { return null; }
}
class AstFunctions {
public static > Function 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,Function super F,? extends T#1>
found: List,Function,String>
reason: cannot infer type-variable(s) F,T#1,T#2
(argument mismatch; Function 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 transform(Iterable,Function super F,? extends T#1>)
T#1 extends Object declared in method transform(Iterable,Function super F,? extends T#1>)
T#2 extends ASTNode> declared in method 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.
解决方案
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 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
in your main, the type of the Iterable is 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 {}
class A2 extends ASTNode {}
and in your main:
List extends ASTNode>> list = new List(); //F1 = A1
You could imagine that prettyPrint returns a Function (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!