EVALUATION
There's a recurrent pattern in the JDK that would be invalidated by inferring T as List<Object>:
enum MyEnum {}
class Test {
Class<?> returnClass = null;
void test() {
Object value = Enum.valueOf((Class)Test.class, "");
}
}
Note that the signature of Enum.valueOf is:
<T extends Enum<T>> T valueOf(Class<T>, String)
In this case 15.12.2.7 fails (as the first argument type is raw) - no type is inferred from actual arguments; 15.12.2.8 derives the following set of constraints for T:
T <: Enum<T> (from declared bound)
T <: Object (from expected return type)
This lead to T = glb(Enum<T>, Object) = Enum<T>. If we replace T by Object, then we end up with Enum<Object> which no longer respect the constraints on T (inferred type for T=X should be a subtype of Enum<X>). As a result this method call cannot be type-checked.
One way out is to replace any F-bounded type variable on the RHS with ? thus giving:
T <: Enum<?>
T <: Object
Another way out of this is to infer T = #1, where ub(#1) = Enum<#1>
This has the advantage of (i) not being a straight type-variable trying to escape its context and (ii) respecting the bound constraints; in fact we have that
#1 <: [T:=#1] Enum<T> = Enum<#1>
ub(#1) = Enum<#1> <: Enum<#1> -> OK!
|
EVALUATION
Given:
static <T, U extends java.util.List<T>> void foo() { return; }
Foo.foo();
the JLS does infer U to be List<T>, and then T to be Object. As Philippe says, U should be List<Object>. 15.12.2.8 should say:
"Any remaining type variable Tr that has not yet been inferred is then inferred to have type Object. If a previously inferred type Tp uses Tr, then Tp is now inferred to be Tp[Tr=Object]."
Sidebar: Philippe seems to imply that the example above gives a different inference result than:
static <T, U extends java.util.List<T>> U foo() { return null; }
String s = (String) Foo.foo();
but javac 1.6 infers Object for both T and U in both examples. Here, we compute U=glb(String, List<T>), so Object is the only possibility.
In the F-bounded case of:
static <T, U extends java.util.List<U>> U foo() { return null; }
String s = (String) Foo.foo();
we have U<:String and U<:List<U>, and need to compute U=glb(String,List<U>), which is ill-formed. The worst case would be:
static <T extends java.util.List<U>, U extends java.util.List<T>> ...
as we would have computations which rely on each other:
T=glb(..., List<U>)
U=glb(..., List<T>)
Ideally, T and U would both become List<Object>, or Object at worst. I propose this additional modification to 15.12.2.8:
"Any equality constraints are resolved, and then, for each remaining constraint of the form Ti<:Uk, the argument Ti is inferred to be glb(U'1..U'k) where U'i is Ui with any uninferred Tj (j<>i) replaced with Object."
|