Javagenerics编译器错误
当用Java做一些不太奇特的东西时,我遇到了一个带有generics的错误,我无法理解为什么它不起作用。 代码是:
package test; import java.util.*; public class TestClass { public static class A extends C{} public static class B extends C{} public static class C{} public static class D{} public static class E{} public static void main(String args[]){ E<D> a = new E<D>(); E<D> b = new E<D>(); E<D> c = new E<D>(); E<D> d = new E<D>(); D e = new D(); D f = new D(); D g = new D(); } }
编译时得到的错误是:
test / TestClass.java:11:不兼容的类型 发现:test.TestClass.E> required:test.TestClass.E> E> a = new E>(); ^ test / TestClass.java:12:不兼容的类型 发现:test.TestClass.E> required:test.TestClass.E> E> b = new E>(); ^ test / TestClass.java:13:不兼容的类型 发现:test.TestClass.E> required:test.TestClass.E> E> c = new E>(); ^ test / TestClass.java:14:不兼容的类型 发现:test.TestClass.E> required:test.TestClass.E> E> d = new E>(); ^ 4个错误
如果E<D>
发现了E<D>
,那肯定会匹配E<D>
E<D>
,对吧? 还是我错过了什么?
也许这会帮助你理解:
ArrayList
这不会编译任何类似的错误。
编辑:咨询: http : //www.ibm.com/developerworks/java/library/j-jtp01255.html
这与之前发布的情况基本相同。 基本上,在generics案例中,您永远不会被允许这样做:
想想这个例子:
ArrayList
这不会编译,因为它不是类型安全的。 您可以添加字符串aList。 您试图将保证为Numbers但可以是任何Number的对象列表分配给仅保证包含对象但可以是任何对象的列表。 如果编译器允许这种情况,它将放宽对允许哪些类型的对象进入列表的限制。 这就是你必须使用通配符?的原因:
ArrayList extends Object> alist = new ArrayList();
编译器ArrayList extends Object>
ArrayList extends Object>
,意思是“某个特定类型的ArrayList”? 我不知道,但我知道扩展了Object。这个ArrayList保证只包含这个未知的元素’?’ 类型,因此只包含对象“。 在这种情况下,编译器将不允许您执行alist.add(2)。 为什么会这样,因为编译器不知道列表元素的类型,并且不能保证允许您将Integer对象插入其中。
你认为D extends Object>
D extends Object>
是D extends C>
的超类型D extends C>
D extends C>
。 但是, List
List
不是List
的子类型List
List
,你应该使用List extends D extends C>>
List extends D extends C>>
List extends D extends C>>
。
你的情况基本上相当于
ArrayList> alist = new ArrayList>();
你有与上面相同的问题,右边的列表只能包含类型为参数为C的D类对象,并且你试图将它分配给一个列表(在左侧)可以包含类的对象D的类型参数可以是任何对象。
因此,如果编译器允许您的代码不是类型安全的,则以下操作将失败。
ArrayList> alist = new ArrayList>(); //< not type safe alist.add(new D); //< oops
简而言之,您的具体示例需要以下内容:
// type parameter of left hand side is ? extends subtype List extends D extends Object>> b = Arrays.asList(new D(), new D()); // type parameter of left hand side is identical List> b = Arrays.asList(new D(), new D()); // type parameter of left hand side is ? extends subtype List extends D extends C>> c = Arrays.asList(new D()); // type parameter of left hand side is identical List> c = Arrays.asList(new D());
希望这可以帮助。
检查Arrays.asList调用返回的对象的类型。 我猜它会返回一个List
extends C>
extends C>
指定类型的上限,这意味着类型必须从C扩展。 extends Object>
显然更通用,因此它不兼容。
想想这个用例。 通过指定上限,您希望实现某个最小接口。 假设您在C中声明了一个doIt()
方法。任何扩展C的类都将具有该方法,但不是每个扩展Object的类(这是Java中的每个类)。
当使用非通配符generics类型T
分配给变量( E
)时,所分配的对象必须具有与其generics类型完全相同的T
(包括T
所有generics类型参数,通配符和非通配符)。 在你的情况下, T
是D
,它与D extends C>
类型不同D extends C>
D extends C>
。
你可以做什么,因为D
可分配给D extends C>
D extends C>
,是使用通配符类型:
E extends D extends C>> a = new E();
这比那更糟糕。 你甚至不能这样做:
List> lDQ = Arrays.asList(new D());
如果您按以下方式更改程序则更清楚:
List> a = Arrays.asList(new D(), new D()); //compiles List> b = a; //error
基本上,你将b
声明为可以接受一般内容( D<
anything >
)的东西 ,但是你分配给它的列表只接受更具体的内容( D<
最近的A和B的公共超类 >
)。
将b
声明为List
List
暗示你可以说b.add(new D
,但它实际上是一个List
List
,所以你不能。
您不能在generics-parameter中inheritance。 假设您有以下参考:
List
现在你指定两个相同的列表:a = b;
如果某些代码执行以下操作:
a.add(new Integer(1));
如果有人执行以下操作会发生什么:
String s = b.get(0);
你会得到一个字符串列表的整数。 那应该不行。 这就是List和List不兼容的原因,尽管可以将String分配给Object-reference。 generics不适用于inheritance。
那就是List
List
基本上定义了一个新类, List
List
。 即使c扩展Object并不意味着List
List
extends List
List
,这将是分配工作所需要的。
虽然本系列文章是针对.NET平台编写的,但对Javagenerics问题的描述仍然适用
如果我们扩展您的示例,我认为这将更加清晰。 让我们在E中加入一些function,并详细说明main方法的第一行:
public static class E{ private final T thing; public void setThing(T thing) { this.thing = thing; } public T getThing() { return thing; } } public static void main(String[] args) { E> a1; E> a2 = new E>(); a1 = a2; // this won't compile, but why? // these things are permissible on a1: a1.setThing(new D()); a2.setThing(new D()); // now let's try doing the same thing to a2: a2.setThing(new D()); a2.setThing(new D()); // oops }
新main方法的最后一行是为什么a1不能设置为a2。 如果编译器允许你这样做,那么你就可以将D 放入一个声明只能容纳D 的容器中。
从原始示例中不明显的原因是因为您没有使用限制性更强的E
不。
<? 扩展C>与<?不一样? extends Object>
如果是这种情况,它也会导致(未说明的)假设 – C扩展Object,并导致说…
C类{public void doSomethingMEaningful(); };
清单<? extends C> allAtSea = new ArrayList <...>(); 清单<? extends Object> allObjects = new ArrayList <...>(); allObjects.add(new Integer(88)); …
allAtSea.addAll(allObjects); …
allAtSea.get(…)doSomethingMeaningful(…)。 //呃哦..这找到了整数88
C ++ FAQ在21.3提供了一个清晰的例子