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议是,避免这样的声明是危险的,它可能没有编译错误,但它是可怕的代码,你可能会添加一些东西,但你也可能会添加错误的东西,这将打破你的代码。