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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (17)

我一直对我的头撞了一阵子,认为也许有些新鲜的眼睛会看到这个问题; 谢谢你的时间。

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.它看起来像直接创建容器test等同于容器test2,但编译器说:

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

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

请注意,我实际上将添加键入的Tbins,这就是为什么我Tbin<Derived>在最后一行中指定的原因。

提问于
用户回答回答于

替换TbinListwith 的定义

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

并确定test2

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

反而会解决问题。

根据你的定义,你最终会得到一个ArrayList<Tbin<T>>T是任何固定类的扩展Base

用户回答回答于

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

存在从参数化类型到参数化类型的捕获转换,其中,对于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

仅捕获“outside”通配符。在Map关键的通配符被捕获,但List元素通配符是没有的。这就是为什么,例如,我们可以添加到a List<List<?>>,但不是a List<?>。通配符的位置是重要的。

携带这种过来TbinList,如果我们有一个ArrayList<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>();

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

扫码关注云+社区