首页 > 解决方案 > 具有冗余类型参数的通用方法

问题描述

我无法理解以下两个方法签名之间的差异。

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>>. Textends Child。所以让我们把它放在一起Class<? extends Parent<? extends Child>>

发现是Class<CAP#1>CAP#1extends Parent<? extends Child>。这一起给了我Class<? extends Parent<? extends Child>>

所以两个签名是一样的?!?


更新:

在 Eclipse 中编译此代码有效。与 javac 相比,Eclipse 编译器将签名视为相同。

标签: javagenerics

解决方案


我也为自己做了很长的解释,在这里;如果你想读它。加上这与捕获转换有关。

简而言之,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;因此协方差成立。

也有人说“有界通配符使类型协变”。


推荐阅读