通用返回类型上限
这是来自第三方库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
示例,您需要记住泛型既不是协变也不是逆变。 这意味着如果X
是Y
的子类型,那么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