通用返回类型上限

这是来自第三方库API的真实示例,但简化了。

用Oracle JDK 8u72编译

考虑这两种方法:

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

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

两人都报告“未经检查的演员”警告 - 我明白了原因。 让我感到困惑的是为什么我可以打电话

Integer x = getCharSequence();

它编译? 编译器应该知道Integer没有实现CharSequence 。 来电

Integer y = getString();

给出一个错误(如预期的那样)

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

有人可以解释为什么这种行为被认为是有效的吗? 它将如何有用?

客户端不知道这个调用是不安全的 - 客户端的代码在没有警告的情况下编译。 为什么编译不会对此提出警告/发布错误?

另外,它与这个例子有什么不同:

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

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

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

按照预期,试图传递List<Integer>会给出错误:

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

如果这被报告为错误,为什么Integer x = getCharSequence(); 是不是?


CharSequence是一个interface 。 因此,即使SomeClass没有实现CharSequence ,创建一个类也是完全可能的

class SubClass extends SomeClass implements CharSequence

因此你可以写

SomeClass c = getCharSequence();

因为推断的类型X是交集类型SomeClass & CharSequence

Integer情况下,这有点奇怪,因为Integer是最终的,但final不会在这些规则中扮演任何角色。 例如,你可以写

<T extends Integer & CharSequence>

另一方面, String不是一个interface ,因此不可能扩展SomeClass以获取String的子类型,因为Java不支持类的多重继承。

通过List示例,您需要记住泛型既不是协变也不是逆变。 这意味着如果XY的子类型,那么List<X>既不是子类型也不是List<Y>的超类型。 由于Integer没有实现CharSequence ,所以你不能在你的doCharSequence方法中使用List<Integer>

你可以,但是要编译

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

如果你有一个像这样返回List<T>的方法:

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

你可以做

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

同样,这是因为推断的类型是Integer & CharSequence ,这是Integer的子类型。

当指定多个边界时,交集类型会隐式发生(例如, <T extends SomeClass & CharSequence> )。

有关更多信息,请参阅JLS中介绍类型边界如何工作的部分。 你可以包含多个接口,例如

<T extends String & CharSequence & List & Comparator>

但只有第一个界限可能是非界面。


编译器在分配X之前推断的类型是Integer & CharSequence 。 这种类型感觉很奇怪,因为Integer是最终的,但它在Java中是完全有效的类型。 然后将它投射到Integer ,这非常合适。

Integer & CharSequence类型有一个可能的值: null 。 通过以下实施:

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

以下任务将起作用:

Integer x = getCharSequence();

由于这个可能的价值,没有理由为什么作业应该是错误的,即使它显然是无用的。 警告会很有用。

真正的问题是API,而不是呼叫站点

事实上,我最近在博客上介绍了这个API设计反模式。 您应该(几乎)从不设计泛型方法来返回任意类型,因为您可以(几乎)不能保证推断的类型将被传递。 像Collections.emptyList()这样的方法是一个例外,在这种情况下,列表的空白(和泛型类型擦除)是为什么<T>任何推断都可以工作的原因:

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

上一篇: Generic return type upper bound

下一篇: braces if block variable definition gives an error