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

2022-08-31 20:39:50

我已经用头撞了这个一段时间,并认为也许一些新的眼睛会看到这个问题;感谢您抽出宝贵时间接受采访。

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>());
  }
}

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

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

我该如何写,所以最后一行是可以接受的?TbinTbinList

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


答案 1

发生这种情况是因为捕获转换的工作方式:

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

  • 如果 是形式的通配符类型参数,则是一个新类型变量 [...]。Ti? extends BiSi

捕获转换不以递归方式应用。

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

    Map<?, List<?>>
//      │  │    └ no capture (not applied recursively)
//      │  └ T2 is not a wildcard
//      └ T1 is a wildcard

仅捕获“外部”通配符。捕获键通配符,但不捕获元素通配符。例如,这就是为什么我们可以添加到 , 但不能添加到 .通配符的位置才是最重要的。MapListList<List<?>>List<?>

如果我们有一个,通配符是在一个没有被捕获的地方,但是如果我们有一个,通配符在它被捕获的地方。TbinListArrayList<Tbin<?>>TbinList<?>

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

ArrayList<Tbin<? extends Base>> test3 = new TbinList<>();

我们得到这个错误:

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

因此,没有办法让它按原样工作。需要更改其中一个类声明。


此外,以这种方式思考。

假设我们有:

class Derived1 extends Base {}
class Derived2 extends Base {}

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

TbinList<? extends Base> test4 = new TbinList<Derived1>();

我们是否应该能够添加一个 ?不,这将是堆污染。我们最终可能会以 s 在 .Tbin<Derived2>test4Derived2TbinList<Derived1>


答案 2

将 的定义替换为TbinList

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

并定义test2

TbinList<Base> test2 = new TbinList<>();

相反,它将解决问题。

有了你的定义,你最终会得到一个,其中T是任何固定的类扩展。ArrayList<Tbin<T>>Base


推荐