我想这样做:
<T extends java.util.Date> T a(@Nonnull T... dates) {
return b(dates); // compile error
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
Run Code Online (Sandbox Code Playgroud)
但是除非我在其中插入强制类型转换,否则它无法编译a:
<T extends java.util.Date> T a(@Nonnull T... dates) {
return (T) b(dates); // warning about unsafe cast in IntelliJ
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,如果我从a它的泛型中删除它:
java.util.Date a(java.util.Date... dates) {
return b(dates);
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
Run Code Online (Sandbox Code Playgroud)
而且,如果我将原始代码移植到Kotlin,它也可以工作(这使我认为这是Java的限制,而不是根本上不可知的东西):
fun <T: java.util.Date> a(dates: Array<T>): T {
return b(dates);
}
fun <T: Comparable<T>> b(comparables: Array<T>): T {
return comparables[0];
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:Java的类型系统有什么特殊之处可阻止此类型的编译?在我看来,Java编译器可以只在后台插入强制类型转换(据我了解,这就是在其他情况下实现泛型的方式)。
无需强制转换即可将其编译。相反,可以在指定T必须扩展时使用有界通配符Comparable<T>:
<T extends java.util.Date> T a(T... dates) {
return b(dates); // compiles fine
}
<T extends Comparable<? super T>> T b(T... comparables) {
return comparables[0];
}
Run Code Online (Sandbox Code Playgroud)
请注意,Comparable<? super T>而不是Comparable<T>。
正如Johannes Kuhn在他的评论中所指出的,Datewill 的子类隐式实现Comparable<Date>了Comparable<DateSubclass>,因此需要Comparable<? super T>。
有关更多信息,请参见:什么是PECS(生产者扩展了超级消费者)?
问题在于,可以使用以下类来调用第一个方法:
class MyDate extends Date {}
Run Code Online (Sandbox Code Playgroud)
然后,T在第一种方法中推断为MyDate,但T在第二种方法中不能推断为MyDate,因为MyDate它不扩展Comparable<MyDate>-它仅扩展Comparable<Date>...
因此,编译错误的根本原因是Java泛型是不变的。这就是为什么Kotlin(其泛型支持声明站点差异)接受代码而没有问题的原因。
要在Java中解决此问题,可以使用通配符类型,如Jacoc G.的答案所示。
| 归档时间: |
|
| 查看次数: |
114 次 |
| 最近记录: |