java - 具有冗余类型参数的通用方法
问题描述
我无法理解以下两个方法签名之间的差异。
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);
abstract <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz);
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
需要?T 已经在类型参数 ( ) 中扩展了 Child ,T extends Child
这个附加? extends
项对我来说似乎是多余的。
更新:
以 javac 开头-DverboseResolution=all
的描述性更强,但仍然令人困惑
…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>>
. T
是extends Child
。所以让我们把它放在一起Class<? extends Parent<? extends Child>>
。
发现是Class<CAP#1>
。CAP#1
是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 Child
一点<:
(符号在第二个链接中解释)? extends T
;因此协方差成立。
也有人说“有界通配符使类型协变”。
推荐阅读
- tfs - 按迭代开始日期对 TFS 查询进行排序
- swift - 如何在 TVOS 的 TVDigitEntryViewController 的完成处理程序中获取输入的引脚?
- eclipse - 启动 Jasper Report Studio 并通过命令行导入项目
- java - 如何在 Java 中定义 anx 3 大数组并向其添加元素?
- macos - 当我从电子加载它时,如何调试我的 Safari 应用程序扩展?
- sql-server - 数据仓库维度设计技术
- html - CSS @page 不适用于更新打印 html 页面的页眉和页脚
- swift - 在 iOS 平台上测试应用程序时,如何检测 Flutter 应用程序的运行时错误并记录它们?
- java - 使用 Java 中的多线程读取/处理一个 CSV 文件
- azure - 为什么红隼一直抛出这个调试 ssl 异常?