Why is it that Java can infer the common ancestor of multiple upper-bounded types, but not of lower-bounded types?
More specifically, consider the following examples:
static class Test {
static <T> T pick(T one, T two) {
return two;
}
static void testUpperBound() {
List<? extends Integer> extendsInteger = new ArrayList<>();
// List<? extends Integer> is treated as a subclass of List<? extends Number>
List<? extends Number> extendsNumber = extendsInteger;
// List<? extends Number> is inferred as the common superclass
extendsNumber = pick(extendsInteger, extendsNumber);
}
static void testLowerBound() {
List<? super Number> superNumber = new ArrayList<>();
// List<? super Number> is treated as a subclass of List<? super Integer>
List<? super Integer> superInteger = superNumber;
// The inferred common type should be List<? super Integer>,
// but instead we get a compile error:
superInteger = pick(superNumber, superInteger);
// It only compiles with an explicit type argument:
superInteger = Test.<List<? super Integer>>pick(superNumber, superInteger);
}
}
Consider the following example: MyClass<Integer> myObject = new MyClass<>(""); In this example, the compiler infers the type Integer for the formal type parameter, X , of the generic class MyClass<X> . It infers the type String for the formal type parameter, T , of the constructor of this generic class.
A lower bounded wildcard is expressed using the wildcard character ('? '), following by the super keyword, followed by its lower bound: <? super A>. Note: You can specify an upper bound for a wildcard, or you can specify a lower bound, but you cannot specify both.
Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.
Java SE 7 supports limited type inference for generic instance creation; you can only use type inference if the parameterized type of the constructor is obvious from the context.
I think I can explain why Java differentiates between a lower-bounded and upper-bounded type.
Trying to infer a common lower bound can fail when incompatible bounds are used, for example Integer
and Long
. When we're using an upper bound, it's always possible to find some common upper bound, in this case List<? extends Number>
. But there's no common lower bound of List<? super Integer>
and List<? super Long>
. The only safe option in case of such a conflict would be to return List<? extends Object>
, synonymous with List<?>
, meaning "a List
of unknown type".
Now, arguably we could have resorted to that only when there actually are conflicting bounds, as opposed to the case in my question. But maybe it was decided to take the easy way out and not assume there's a common lower bound unless explicitly specified.
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