Generic return type upper bound

This is a real-world example from a 3rd party library API, but simplified.

Compiled with Oracle JDK 8u72

Consider these two methods:

<X extends CharSequence> X getCharSequence() {
    return (X) "hello";
}

<X extends String> X getString() {
    return (X) "hello";
}

Both report an "unchecked cast" warning - I get why. The thing that baffles me is why can I call

Integer x = getCharSequence();

and it compiles? The compiler should know that Integer does not implement CharSequence . The call to

Integer y = getString();

gives an error (as expected)

incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String

Can someone explain why would this behaviour be considered valid? How would it be useful?

The client does not know that this call is unsafe - the client's code compiles without warning. Why wouldn't the compile warn about that / issue an error?

Also, how is it different from this example:

<X extends CharSequence> void doCharSequence(List<X> l) {
}

List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles

List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error

Trying to pass List<Integer> gives an error, as expected:

method doCharSequence in class generic.GenericTest cannot be applied to given types;
  required: java.util.List<X>
  found: java.util.List<java.lang.Integer>
  reason: inference variable X has incompatible bounds
    equality constraints: java.lang.Integer
    upper bounds: java.lang.CharSequence

If that is reported as an error, why Integer x = getCharSequence(); isn't?


CharSequence is an interface . Therefore even if SomeClass does not implement CharSequence it would be perfectly possible to create a class

class SubClass extends SomeClass implements CharSequence

Therefore you can write

SomeClass c = getCharSequence();

because the inferred type X is the intersection type SomeClass & CharSequence .

This is a bit odd in the case of Integer because Integer is final, but final doesn't play any role in these rules. For example you can write

<T extends Integer & CharSequence>

On the other hand, String is not an interface , so it would be impossible to extend SomeClass to get a subtype of String , because java does not support multiple-inheritance for classes.

With the List example, you need to remember that generics are neither covariant nor contravariant. This means that if X is a subtype of Y , List<X> is neither a subtype nor a supertype of List<Y> . Since Integer does not implement CharSequence , you cannot use List<Integer> in your doCharSequence method.

You can, however get this to compile

<T extends Integer & CharSequence> void foo(List<T> list) {
    doCharSequence(list);
}  

If you have a method that returns a List<T> like this:

static <T extends CharSequence> List<T> foo() 

you can do

List<? extends Integer> list = foo();

Again, this is because the inferred type is Integer & CharSequence and this is a subtype of Integer .

Intersection types occur implicitly when you specify multiple bounds (eg <T extends SomeClass & CharSequence> ).

For further information, here is the part of the JLS where it explains how type bounds work. You can include multiple interfaces, eg

<T extends String & CharSequence & List & Comparator>

but only the first bound may be a non-interface.


The type that is inferred by your compiler prior to the assignment for X is Integer & CharSequence . This type feels weird, because Integer is final, but it's a perfectly valid type in Java. It is then cast to Integer , which is perfectly OK.

There is exactly one possible value for the Integer & CharSequence type: null . With the following implementation:

<X extends CharSequence> X getCharSequence() {
    return null;
}

The following assignment will work:

Integer x = getCharSequence();

Because of this possible value, there's no reason why the assignment should be wrong, even if it is obviously useless. A warning would be useful.

The real problem is the API, not the call site

In fact, I've recently blogged about this API design anti pattern. You should (almost) never design a generic method to return arbitrary types because you can (almost) never guarantee that the inferred type will be delivered. An exception are methods like Collections.emptyList() , in case of which the emptiness of the list (and generic type erasure) is the reason why any inference for <T> will work:

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}
链接地址: http://www.djcxy.com/p/59504.html

上一篇: 方法引用不编译

下一篇: 通用返回类型上限