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>()); ^ 
 我如何写Tbin和TbinList使最后一行可以接受? 
 请注意,实际上我将添加键入的Tbin ,这就是为什么我在最后一行中指定了Tbin<Derived>原因。 
发生这种情况是因为捕捉转换的方式:
存在从参数化types
G<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吗? 不,这将是堆污染。 我们可能会以Derived2在TbinList<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议是,避免这样的声明是危险的,它可能没有编译错误,但它是可怕的代码,你可能会添加一些东西,但你也可能会添加错误的东西,这将打破你的代码。