简体   繁体   English

具有冗余类型参数的通用方法

[英]Generic method with redundant type parameter

I have problems understanding the differences between the following two method signatures.我无法理解以下两个方法签名之间的差异。

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> {}
}

Compiling this code gives the following error (Tested with 1.8.0_271, 11.0.9 and 15.0.1).编译此代码会出现以下错误(使用 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>>)

Why is ? extends为什么是? extends ? extends needed?需要? extends吗? T already extends Child in the type parameter ( T extends Child ) and this additional ? extends T 已经在类型参数中扩展了 Child ( T extends Child ) 并且这个附加的? extends ? extends seems redundant to me. ? extends对我来说似乎是多余的。


Update:更新:

Starting javac with -DverboseResolution=all is a bit more descriptive but still confusing使用-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>

Interpreting this:解释这个:

Required is Class<? extends Parent<T>>要求是Class<? extends Parent<T>> Class<? extends Parent<T>> . Class<? extends Parent<T>> T is extends Child . Textends Child So let's put this together into 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>>

Found is Class<CAP#1> .找到的是Class<CAP#1> CAP#1 is extends Parent<? extends Child> CAP#1extends Parent<? extends Child> extends Parent<? extends Child> . extends Parent<? extends Child> Together this gives me Class<? extends Parent<? extends Child>>这一起给了我Class<? extends Parent<? extends Child>> Class<? extends Parent<? extends Child>>

So both signatures are the same???所以两个签名是一样的???


Update:更新:

Compiling this code in Eclipse works.在 Eclipse 中编译此代码有效。 In contrast to javac the Eclipse compiler treats the signatures as the same.与 javac 相比,Eclipse 编译器将签名视为相同。

The really long explanation I did for myself too, is here ;我也为自己做了很长的解释, 在这里 if you want to read it.如果你想读它。 Plus this is related to capture conversion .加上这与捕获转换有关。

In short, the compiler simply can't prove that a wildcard capture is compatible with T , in your case.简而言之,在您的情况下,编译器根本无法证明通配符捕获与T兼容。 If you run with: javac --debug=verboseResolution=all... , you will see an error like:如果您使用: javac --debug=verboseResolution=all...运行,您将看到如下错误:

....
(argument mismatch; Class<CAP#1> cannot be converted to Class<? extends Parent<T>>)

So a capture conversion ( CAP#1 ) can't simply satisfy the compiler here.所以捕获转换CAP#1 )不能简单地满足这里的编译器。 The way to make it work is to introduce one more capture, via ...<? extends T>使其工作的方法是通过...<? extends T> ...<? extends T> . ...<? extends T> This can be proven by the compiler that ? extends Child这可以通过编译器证明? extends Child ? extends Child is <: (notation explained in the second link) of ? extends T ? extends Child is <: (第二个链接中解释的符号) ? extends T ? extends T ; ? extends T ; thus covariance is established.因此协方差成立。

It is also said that a "bounded wildcard makes the type covariant".也有人说“有界通配符使类型协变”。

The type Class<Parent<? extends Child>>类型Class<Parent<? extends Child>> Class<Parent<? extends Child>> can be passed to the first signature and cannot be passed to the second, and since Class<Parent<? extends Child>> Class<Parent<? extends Child>>可以传递给第一个签名,而不能传递给第二个签名,并且由于Class<Parent<? extends Child>> Class<Parent<? extends Child>> can be assigned to a Class<? extends Parent<? extends Child>> Class<Parent<? extends Child>>可以分配给一个Class<? extends Parent<? extends Child>> Class<? extends Parent<? extends Child>> Class<? extends Parent<? extends Child>> , it follows that 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>> also cannot be passed to the second signature. Class<? extends Parent<? extends Child>>也不能传递给第二个签名。

So the question reduces to why Class<Parent<? extends Child>>所以问题归结为为什么Class<Parent<? extends Child>> Class<Parent<? extends Child>> cannot be passed to the second signature ( <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz) ). Class<Parent<? extends Child>>不能传递给第二个签名( <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz) )。 We can see that that is because Parent<? extends Child>我们可以看到那是因为Parent<? extends Child> Parent<? extends Child> cannot a subtype of Parent<T> , no matter what specific type you choose for T . Parent<? extends Child>不能是Parent<T>的子类型,无论您为T选择什么特定类型。 ( Parent<T> is a subtype of Parent<? extends Child> , but not the other way around.) Parent<T>Parent<? extends Child>的子类型,但反之则不然。)

Consider the equivalent question of why List<Parent<? extends Child>>考虑一下为什么List<Parent<? extends Child>> List<Parent<? extends Child>> cannot be passed to a signature of <T extends Child> Parent<T> error(List<? extends Parent<T>> clazz) . List<Parent<? extends Child>>不能传递给<T extends Child> Parent<T> error(List<? extends Parent<T>> clazz)的签名。 A List<Parent<? extends Child>>一个List<Parent<? extends Child>> List<Parent<? extends Child>> allows you to add both Parent<ChildA> and Parent<ChildB> elements into the same list at the same time. List<Parent<? extends Child>>允许您同时将Parent<ChildA>Parent<ChildB>元素添加到同一个列表中。 In other words, it is a "heterogenous list".换句话说,它是一个“异构列表”。 On the other hand, for any given choice of T , a List<Parent<T>> (or List<SubclassOfParent<T>> ) is a list that can only contain Parent objects of a single type argument.另一方面,对于任何给定的T选择, List<Parent<T>> (或List<SubclassOfParent<T>> )是一个只能包含单个类型参数的Parent对象的列表。 It is a "homogenous list".这是一个“同质列表”。 Even if generics allows you to operate on homogenous lists of various type arguments generically, homogenous lists are still different beasts from heterogenous lists.即使 generics 通常允许您对各种类型的同构列表 arguments 进行操作,同构列表仍然是与异构列表不同的野兽。 The two are incompatible.两者不相容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM