Java Generics Puzzler,扩展一个类并使用通配符

use*_*663 51 java generics bounded-wildcard

我一直在打击这个问题一段时间,并认为可能会有一些新鲜的眼睛看到这个问题; 谢谢你的时间.

import java.util.*;

class Tbin<T> extends ArrayList<T> {}
class TbinList<T> extends ArrayList<Tbin<T>> {}

class Base {}
class Derived extends Base {}

public class Test {
  public static void main(String[] args) {
    ArrayList<Tbin<? extends Base>> test = new ArrayList<>();
    test.add(new Tbin<Derived>());

    TbinList<? extends Base> test2 = new TbinList<>();
    test2.add(new Tbin<Derived>());
  }
}
Run Code Online (Sandbox Code Playgroud)

使用Java 8.在我看来,直接创建容器test就等同于容器test2,但编译器说:

Test.java:15: error: no suitable method found for add(Tbin<Derived>)
    test2.add(new Tbin<Derived>());
         ^
Run Code Online (Sandbox Code Playgroud)

我怎么写Tbin,TbinList所以最后一行是可以接受的?

请注意,我实际上将添加类型Tbins,这就是我Tbin<Derived>在最后一行中指定的原因.

Rad*_*def 37

这是因为捕获转换的工作方式:

存在从参数化类型到参数化类型的捕获转换,其中,对于1≤i≤n:G<T1,...,Tn>G<S1,...,Sn>

  • 如果是表单的通配符类型参数,那么它是一个新类型变量[...].Ti? extends BiSi

捕获转换不会递归应用.

注意结束位.所以,这意味着,给定这样的类型:

    Map<?, List<?>>
//      ?  ?    ? no capture (not applied recursively)
//      ?  ? T2 is not a wildcard
//      ? T1 is a wildcard
Run Code Online (Sandbox Code Playgroud)

只捕获"外部"通配符.在Map关键的通配符被捕获,但List元素通配符是没有的.这就是为什么,例如,我们可以添加到a List<List<?>>,而不是a List<?>.通配符的位置才是最重要的.

携带这种过来TbinList,如果我们有一个ArrayList<Tbin<?>>,通配符是在它不会捕捉的地方,但如果我们有TbinList<?>,通配符是在它被捕获的地方.

正如我在评论中提到的,一个非常有趣的测试是这样的:

ArrayList<Tbin<? extends Base>> test3 = new TbinList<>();
Run Code Online (Sandbox Code Playgroud)

我们收到此错误:

error: incompatible types: cannot infer type arguments for TbinList<>
    ArrayList<Tbin<? extends Base>> test3 = new TbinList<>();
                                                        ^
    reason: no instance(s) of type variable(s) T exist so that
            TbinList<T> conforms to ArrayList<Tbin<? extends Base>>
Run Code Online (Sandbox Code Playgroud)

所以没有办法让它按原样运作.其中一个类声明需要更改.


另外,这样考虑一下.

假设我们有:

class Derived1 extends Base {}
class Derived2 extends Base {}
Run Code Online (Sandbox Code Playgroud)

由于通配符允许子类型化,我们可以这样做:

TbinList<? extends Base> test4 = new TbinList<Derived1>();
Run Code Online (Sandbox Code Playgroud)

如果我们能够将添加Tbin<Derived2>test4?不,这将是堆污染.我们可能最终会Derived2在a中漂浮TbinList<Derived1>.


tyn*_*ynn 10

替换TbinListwith 的定义

class TbinList<T> extends ArrayList<Tbin<? extends T>> {}
Run Code Online (Sandbox Code Playgroud)

并确定test2

TbinList<Base> test2 = new TbinList<>();
Run Code Online (Sandbox Code Playgroud)

相反会解决问题.

根据您的定义,您最终得到的ArrayList<Tbin<T>>地方是T是任何固定类扩展Base.


use*_*663 1

好的,答案如下:

import java.util.*;

class Tbin<T> extends ArrayList<T> {}
class TbinList<T> extends ArrayList<Tbin<? extends T>> {}

class Base {}
class Derived extends Base {}

public class Test {
  public static void main(String[] args) {

    TbinList<Base> test3 = new TbinList<>();
    test3.add(new Tbin<Derived>());

  }
}
Run Code Online (Sandbox Code Playgroud)

正如我所料,我一看到它就很明显。但经过一番折腾才到达这里。如果您只看工作代码,Java 泛型看起来很简单。

谢谢大家的参谋。

  • 它显示了tynn首先在我这边发布的答案。无论如何,这只是例行公事的评论。有时用户会因为不知道接受而重新发帖。 (2认同)