[英]Generic method with redundant type parameter
我无法理解以下两个方法签名之间的差异。
abstract class Example {
void test() {
Class<? extends Parent<? extends Child>> clazz = null;
works(clazz);
error(clazz); // Error
}
abstract <T extends Child> Parent<T> works(Class<? extends Parent<? extends T>> clazz); // Method1
abstract <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz); // Method2
interface Child {}
interface Parent<U extends Child> {}
}
编译此代码会出现以下错误(使用 1.8.0_271、11.0.9 和 15.0.1 测试)。
…/src/main/java/Example.java:6:5
java: method error in class Example cannot be applied to given types;
required: java.lang.Class<? extends Example.Parent<T>>
found: java.lang.Class<capture#1 of ? extends Example.Parent<? extends Example.Child>>
reason: cannot infer type-variable(s) T
(argument mismatch; java.lang.Class<capture#1 of ? extends Example.Parent<? extends Example.Child>> cannot be converted to java.lang.Class<? extends Example.Parent<T>>)
为什么是? extends
需要? extends
吗? T 已经在类型参数中扩展了 Child ( T extends Child
) 并且这个附加的? extends
? extends
对我来说似乎是多余的。
更新:
使用-DverboseResolution=all
启动 javac 更具描述性,但仍然令人困惑
…src/main/java/Example.java:6: error: method error in class Example cannot be applied to given types;
error(clazz); // Error
^
required: Class<? extends Parent<T>>
found: Class<CAP#1>
reason: cannot infer type-variable(s) T
(argument mismatch; Class<CAP#1> cannot be converted to Class<? extends Parent<T>>)
where T is a type-variable:
T extends Child declared in method <T>error(Class<? extends Parent<T>>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Parent<? extends Child> from capture of ? extends Parent<? extends Child>
解释这个:
要求是Class<? extends Parent<T>>
Class<? extends Parent<T>>
。 T
是extends Child
。 所以让我们把它放在Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
。
找到的是Class<CAP#1>
。 CAP#1
是extends Parent<? extends Child>
extends Parent<? extends Child>
。 这一起给了我Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
所以两个签名是一样的???
更新:
在 Eclipse 中编译此代码有效。 与 javac 相比,Eclipse 编译器将签名视为相同。
我也为自己做了很长的解释, 在这里; 如果你想读它。 加上这与捕获转换有关。
简而言之,在您的情况下,编译器根本无法证明通配符捕获与T
兼容。 如果您使用: javac --debug=verboseResolution=all...
运行,您将看到如下错误:
....
(argument mismatch; Class<CAP#1> cannot be converted to Class<? extends Parent<T>>)
所以捕获转换( CAP#1
)不能简单地满足这里的编译器。 使其工作的方法是通过...<? extends T>
...<? extends T>
。 这可以通过编译器证明? extends Child
? extends Child
is <:
(第二个链接中解释的符号) ? extends T
? extends T
; 因此协方差成立。
也有人说“有界通配符使类型协变”。
类型Class<Parent<? extends Child>>
Class<Parent<? extends Child>>
可以传递给第一个签名,而不能传递给第二个签名,并且由于Class<Parent<? extends Child>>
Class<Parent<? extends Child>>
可以分配给一个Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
,它遵循Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
Class<? extends Parent<? extends Child>>
也不能传递给第二个签名。
所以问题归结为为什么Class<Parent<? extends Child>>
Class<Parent<? extends Child>>
不能传递给第二个签名( <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz)
)。 我们可以看到那是因为Parent<? extends Child>
Parent<? extends Child>
不能是Parent<T>
的子类型,无论您为T
选择什么特定类型。 ( Parent<T>
是Parent<? extends Child>
的子类型,但反之则不然。)
考虑一下为什么List<Parent<? extends Child>>
List<Parent<? extends Child>>
不能传递给<T extends Child> Parent<T> error(List<? extends Parent<T>> clazz)
的签名。 一个List<Parent<? extends Child>>
List<Parent<? extends Child>>
允许您同时将Parent<ChildA>
和Parent<ChildB>
元素添加到同一个列表中。 换句话说,它是一个“异构列表”。 另一方面,对于任何给定的T
选择, List<Parent<T>>
(或List<SubclassOfParent<T>>
)是一个只能包含单个类型参数的Parent
对象的列表。 这是一个“同质列表”。 即使 generics 通常允许您对各种类型的同构列表 arguments 进行操作,同构列表仍然是与异构列表不同的野兽。 两者不相容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.