Ada*_*lik 170 java generics java-8
这是来自第三方库API的真实示例,但已简化.
使用Oracle JDK 8u72编译
考虑这两种方法:
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
Run Code Online (Sandbox Code Playgroud)
两者都报告了"未经检查的演员"警告 - 我明白了.困扰我的是我为什么打电话
Integer x = getCharSequence();
Run Code Online (Sandbox Code Playgroud)
它编译?编译器应该知道Integer
没有实现CharSequence
.打电话给
Integer y = getString();
Run Code Online (Sandbox Code Playgroud)
给出错误(如预期的那样)
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
试图传递List<Integer>
给出错误,如预期的那样:
Run Code Online (Sandbox Code Playgroud)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();
不呢?
Pau*_*ton 183
CharSequence
是一个interface
.因此即使SomeClass
没有实现CharSequence
它也很可能创建一个类
class SubClass extends SomeClass implements CharSequence
Run Code Online (Sandbox Code Playgroud)
所以你可以写
SomeClass c = getCharSequence();
Run Code Online (Sandbox Code Playgroud)
因为推断类型X
是交集类型SomeClass & CharSequence
.
这是Integer
因为Integer
是最终的,但final
在这些规则中没有任何作用,这有点奇怪.例如,你可以写
<T extends Integer & CharSequence>
Run Code Online (Sandbox Code Playgroud)
另一方面,String
不是一个interface
,因此不可能扩展SomeClass
以获得子类型String
,因为java不支持类的多重继承.
通过这个List
例子,你需要记住泛型既不是协变也不是逆变.这意味着如果X
是子类型Y
,List<X>
既不是子类型也不是超类型List<Y>
.由于Integer
未实现CharSequence
,您无法List<Integer>
在您的doCharSequence
方法中使用.
但是,您可以将其编译
<T extends Integer & CharSequence> void foo(List<T> list) {
doCharSequence(list);
}
Run Code Online (Sandbox Code Playgroud)
如果你有一个方法返回一个List<T>
像这样的:
static <T extends CharSequence> List<T> foo()
Run Code Online (Sandbox Code Playgroud)
你可以做
List<? extends Integer> list = foo();
Run Code Online (Sandbox Code Playgroud)
同样,这是因为推断类型是Integer & CharSequence
,这是一个子类型Integer
.
当您指定多个边界(例如<T extends SomeClass & CharSequence>
)时,交叉类型会隐式发生.
欲了解更多信息,这里是JLS的部分地方解释型的边界是如何工作的.您可以包含多个接口,例如
<T extends String & CharSequence & List & Comparator>
Run Code Online (Sandbox Code Playgroud)
但只有第一个边界可能是非界面.
Luk*_*der 59
在分配之前由编译器推断的类型X
是Integer & CharSequence
.这种类型感觉很奇怪,因为它Integer
是最终的,但它是Java中完全有效的类型.然后施展Integer
,这是完全可以的.
该类型只有一个可能的值Integer & CharSequence
:null
.通过以下实现:
<X extends CharSequence> X getCharSequence() {
return null;
}
Run Code Online (Sandbox Code Playgroud)
以下作业将起作用:
Integer x = getCharSequence();
Run Code Online (Sandbox Code Playgroud)
由于这个可能的价值,没有理由为什么分配应该是错误的,即使它显然是无用的.警告会很有用.
事实上,我最近在博客上谈到了这种API设计反模式.你应该(几乎)从不设计一个泛型方法来返回任意类型,因为你(几乎)永远不能保证推断类型将被传递.一个例外是像这样的方法Collections.emptyList()
,如果列表的空白(和泛型类型擦除)是任何推断<T>
将工作的原因:
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
28079 次 |
最近记录: |