I’m going to go out on a limb and say that this error (while it may or may not conform to the updated JLS, which I admit I haven’t read in detail) is due to an inconsistency in type handling by the JDK 8 compiler.
In general the ternary operator used the same type inference as if for a two-argument method which had the formal parameters both based on the same type parameter. For instance:
static <T> T foo(Class<? extends T> clazz, Class<? extends T> clazz2) { return null; }
public static void main(String[] args) {
CharSequence foo2 = foo(String.class, StringBuilder.class);
}
In this example, T
can be inferred to be a capture of ? extends Object & Serializable & CharSequence
. Now similarly, in JDK 7, if we go back to your original example:
CharSequence foo2 = foo(true ? String.class : StringBuilder.class);
This does almost the exact same type inference as above, but in this case consider the ternary operator to be a method as such:
static <T> T ternary(boolean cond, T a, T b) {
if (cond) return a;
else return b;
}
So in this case, if you pass String.class and StringBuilder.class as the parameters, the inferred type of T is (roughly speaking) Class<? extends Object & Serializable & CharSequence>
, which is what we wanted.
In fact you can replace the use of your ternary operator in the original snippet with this method, thus:
public class Foo {
public static void main(String[] args) throws Exception {
//compiles fine in Java 7 and Java 8:
Class<? extends CharSequence> aClass = true ? String.class : StringBuilder.class;
CharSequence foo = foo(aClass);
//Inlining the variable using 'ternary' method:
CharSequence foo2 = foo(ternary(true, String.class, StringBuilder.class));
}
static <T> T foo(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
static <T> T ternary(boolean cond, T a, T b) {
if (cond) return a;
else return b;
}
}
… And now it compiles in Java 7 and 8 (edit: actually it also fails with Java 8! edit again: it now works, Jdk 8u20). What gives? for some reason an equality constraint is now being imposed on T (in the foo method), rather than a lower-bounds constraint. The relevant section of the JLS for Java 7 is 15.12.2.7; for Java 8 there’s a whole new chapter on type inference (chapter 18).
Note that explicitly typing T in the call to ‘ternary’ does allow compilation with Java 7 and 8, but this doesn’t seem like it should be necessary. Java 7 does the right thing, where Java 8 gives an error even though there is an appropriate type that can be inferred for T.