具有冗余类型参数的泛型方法

Phi*_*ppe 5 java generics

我在理解以下两个方法签名之间的差异时遇到问题。

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> {}
}
Run Code Online (Sandbox Code Playgroud)

编译此代码会出现以下错误(使用 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>>)
Run Code Online (Sandbox Code Playgroud)

为什么? extends需要?T 已经在类型参数 ( T extends Child) 中扩展了 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>
Run Code Online (Sandbox Code Playgroud)

解释这个:

必需的是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 编译器将签名视为相同。

new*_*cct 2

该类型Class<Parent<? extends Child>>可以传递给第一个签名,但不能传递给第二个签名,并且由于Class<Parent<? extends Child>>可以分配给 a Class<? extends Parent<? extends Child>>,因此Class<? extends Parent<? extends Child>>也不能传递给第二个签名。

所以问题就归结为为什么Class<Parent<? extends Child>>不能传递给第二个签名(<T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz))。我们可以看到,这是因为无论您选择哪种特定类型,都Parent<? extends Child>不能是 的子类型。(是 的子类型,但反之则不然。)Parent<T>TParent<T>Parent<? extends Child>

List<Parent<? extends Child>>考虑为什么不能传递给 的签名的等效问题<T extends Child> Parent<T> error(List<? extends Parent<T>> clazz)。A允许您同时List<Parent<? extends Child>>Parent<ChildA>和元素添加到同一个列表中。Parent<ChildB>换句话说,它是一个“异构列表”。另一方面,对于 的任何给定选择T,a List<Parent<T>>(或List<SubclassOfParent<T>>)是一个只能包含Parent单一类型参数的对象的列表。这是一个“同质列表”。即使泛型允许您一般地对各种类型参数的同质列表进行操作,同质列表仍然与异质列表不同。两者是不相容的。