Javagenerics益智游戏,扩展类和使用通配符

一直以来我一直在殴打我的头,认为也许有些新鲜的眼睛会看到这个问题; 谢谢你的时间。

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直接创build容器相当于test2的容器,但编译器说:

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

我如何写TbinTbinList使最后一行可以接受?

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

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

存在从参数化typesG<T 1 ,...,T n >到参数化typesG<S 1 ,...,S n >的捕获转换,其中对于1≤i≤n

  • 如果T i是forms的通配types参数? extends B i ? extends B i ,那么S i是一个新鲜的typesvariables。

捕捉转换不是recursion应用的。

注意结束位。 所以,这意味着,给定一个这样的types:

  Map<?, List<?>> // │ │ └ no capture (not applied recursively) // │ └ T 2 is not a wildcard // └ T 1 is a wildcard 

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

如果我们有一个ArrayList<Tbin<?>> ,通配符处在一个不被捕获的地方,但是如果我们有一个TbinList<?> ,那么通配符就处在一个它得到的地方抓获。

正如我在评论中提到的,一个非常有趣的testing是:

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

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

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

我们应该能够添加一个Tbin<Derived2>test4吗? 不,这将是堆污染。 我们可能会以Derived2TbinList<Derived1>浮动。

replaceTbinList的定义

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

并用。定义test2

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

反而会解决这个问题。

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

你正在使用有界通配符( TbinList<? extends Base>> ... )。 这个通配符将阻止你添加任何元素到列表中。 如果您想了解更多信息,请参阅文档中有关通配符的部分。

你可以定义genericstypes如下:

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

然后你会创build像这样的实例:

 TbinList<? extends Base, Tbin<? extends Base>> test2 = new TbinList<>(); test2.add(new Tbin<Derived>()); 

好的,这是答案:

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

正如我所料,有一次我看到它。 但是,很多颠簸到达这里。 Javagenerics看起来很简单,如果你只看工作代码。

谢谢大家,作为一个发声委员会。

你不能添加任何对象到TbinList<? extends Base> TbinList<? extends Base> ,不能保证你要插入到列表中的对象是什么。 当你使用通配符extends时,它应该从test2读取数据

如果你声明为TbinList<? extends Base> TbinList<? extends Base> ,这意味着它是类Base或类Base的任何子类,当初始化它时,使用的是具体类名以外的菱形,这使得你的test2不明显,这使得难以分辨哪些对象可以插入。 我的build议是,避免这样的声明是危险的,它可能没有编译错误,但它是可怕的代码,你可能会添加一些东西,但你也可能会添加错误的东西,这将打破你的代码。