[英]Java generics compiler error: incompatible types
當用Java做一些不太奇特的東西時,我遇到了泛型錯誤,我無法理解為什么它不起作用。 代碼是:
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<T>{}
public static class E<T>{}
public static void main(String args[]){
E<D<? extends C>> a = new E<D<A>>();
E<D<? extends Object>> b = new E<D<? extends C>>();
E<D<? extends A>> c = new E<D<A>>();
E<D<? super A>> d = new E<D<A>>();
D<? extends C> e = new D<A>();
D<? extends A> f = new D<A>();
D<? extends A> g = new D<A>();
}
}
編譯時得到的錯誤是:
test/TestClass.java:11: incompatible types found : test.TestClass.E<test.TestClass.D<test.TestClass.A>> required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>> E<D<? extends C>> a = new E<D<A>>(); ^ test/TestClass.java:12: incompatible types found : test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>> required: test.TestClass.E<test.TestClass.D<? extends java.lang.Object>> E<D<? extends Object>> b = new E<D<? extends C>>(); ^ test/TestClass.java:13: incompatible types found : test.TestClass.E<test.TestClass.D<test.TestClass.A>> required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.A>> E<D<? extends A>> c = new E<D<A>>(); ^ test/TestClass.java:14: incompatible types found : test.TestClass.E<test.TestClass.D<test.TestClass.A>> required: test.TestClass.E<test.TestClass.D<? super test.TestClass.A>> E<D<? super A>> d = new E<D<A>>(); ^ 4 errors
如果E<D<? extends C>>
發現了E<D<? extends C>>
,那肯定會匹配E<D<? extends Object>>
E<D<? extends Object>>
,對嗎? 還是我錯過了什么?
也許這會幫助你理解:
ArrayList<Object> aList = new ArrayList<String>();
這不會編譯任何類似的錯誤。
編輯:咨詢: http : //www.ibm.com/developerworks/java/library/j-jtp01255.html
這與之前發布的情況基本相同。 基本上,在泛型案例中,您永遠不會被允許這樣做:
想想這個例子:
ArrayList<Object> alist = new ArrayList<Number>();
這不會編譯,因為它不是類型安全的。 你可以添加Strings aList。 您試圖將保證為Numbers但可以是任何Number的對象列表分配給僅保證包含對象但可以是任何對象的列表。 如果編譯器允許這種情況,它將放寬對允許哪些類型的對象進入列表的限制。 這就是你必須使用通配符?的原因:
ArrayList<? extends Object> alist = new ArrayList<Number>();
編譯器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<D<? extends Object>>
List<D<? extends Object>>
不是List<D<? extends C>>
的子類型List<D<? extends C>>
List<D<? extends C>>
,你應該使用List<? extends D<? extends C>>
List<? extends D<? extends C>>
List<? extends D<? extends C>>
。
你的情況基本上相當於
ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>();
你有與上面相同的問題,右邊的列表只能包含類型為C的類D的對象,並且你試圖將它分配給一個列表(在左側)可以包含類的對象D的類型參數可以是任何對象。
因此,如果編譯器允許您的代碼不是類型安全的,則以下操作將失敗。
ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>(); //< not type safe
alist.add(new D<Number>); //< oops
簡而言之,您的具體示例需要以下內容:
// type parameter of left hand side is ? extends subtype
List<? extends D<? extends Object>> b = Arrays.asList(new D<A>(), new D<B>());
// type parameter of left hand side is identical
List<D<? extends C>> b = Arrays.asList(new D<A>(), new D<B>());
// type parameter of left hand side is ? extends subtype
List<? extends D<? extends C>> c = Arrays.asList(new D<A>());
// type parameter of left hand side is identical
List<D<A>> c = Arrays.asList(new D<A>());
希望這可以幫助。
檢查Arrays.asList調用返回的對象的類型。 我猜它會返回List <D <? 擴展C >>對象,它不能轉換為List <D <? extends Object >>。
<? extends C>
<? extends C>
指定的類型的上限,這意味着類型必須從下延伸<? extends Object>
<? extends Object>
顯然更為通用,因此它不兼容。
想想這個用例。 通過指定上限,您希望實現某個最小接口。 假設你有一個在C中聲明的doIt()
方法。任何擴展C的類都會有這個方法但不是每個擴展Object的類(那是Java中的每個類)。
當使用非通配符泛型類型T
分配給變量( E<T>
)時,所分配的對象必須具有與其泛型類型完全相同的T
(包括T
所有泛型類型參數,通配符和非通配符)。 在你的情況下, T
是D<A>
,它與D<? extends C>
類型不同D<? extends C>
D<? extends C>
。
你可以做什么,因為D<A>
可分配給D<? extends C>
D<? extends C>
,是使用通配符類型:
E<? extends D<? extends C>> a = new E<D<A>();
它比那更糟糕。 你甚至不能這樣做:
List<D<?>> lDQ = Arrays.asList(new D<A>());
如果您按如下方式更改程序則更清楚:
List<D<? extends C>> a = Arrays.asList(new D<A>(), new D<B>()); //compiles
List<D<? extends Object>> b = a; //error
基本上,你將b
聲明為可以接受一般內容( D<
anything >
)的東西 ,但是你分配給它的列表只接受更具體的內容( D<
最近的A和B的公共超類 >
)。
將b
聲明為List<D<? extends Object>>
List<D<? extends Object>>
意味着你可以說b.add(new D<String>())
,但它實際上是一個List<D<? extends C>>
List<D<? extends C>>
,所以你不能。
那個是List<D<? extends Object>>
List<D<? extends Object>>
基本上定義了一個新類, List<D<? extends C>>
List<D<? extends C>>
。 即使c擴展Object並不意味着List<D<? extends C>>
List<D<? extends C>>
extends List<D<? extends Object>>
List<D<? extends Object>>
,這將是分配工作所需要的。
雖然這一系列文章是針對.NET平台編寫的,但問題的描述仍然適用於Java泛型
您不能在generics-parameter中繼承。 假設您有以下參考:
List<Object> a;
List<String> b;
現在你分配兩個相同的列表:a = b;
如果某些代碼執行以下操作:
a.add(new Integer(1));
如果有人執行以下操作會發生什么:
String s = b.get(0);
你會得到一個字符串列表的整數。 那應該不行。 這就是List和List不兼容的原因,盡管可以將String分配給Object-reference。 泛型不適用於繼承。
如果我們擴展你的例子,我認為這將更加清楚。 讓我們在E中加入一些功能並詳細說明main方法的第一行:
public static class E<T>{
private final T thing;
public void setThing(T thing) {
this.thing = thing;
}
public T getThing() {
return thing;
}
}
public static void main(String[] args) {
E<D<? extends C>> a1;
E<D<A>> a2 = new E<D<A>>();
a1 = a2; // this won't compile, but why?
// these things are permissible on a1:
a1.setThing(new D<A>());
a2.setThing(new D<B>());
// now let's try doing the same thing to a2:
a2.setThing(new D<A>());
a2.setThing(new D<B>()); // oops
}
新main方法的最后一行是為什么a1不能設置為a2。 如果編譯器允許你這樣做,那么你就可以將D <B>放入一個聲明只能容納D <A>的容器中。
原始示例中不明顯的原因是因為您沒有使用限制性更強的E <D <A >>類型創建引用該對象的單獨變量。 但希望現在你可以看到,如果存在這樣的引用,它的類型安全性將受到你試圖完成的任務的影響。
不。
<? 擴展C>與<?不一樣 extends Object>
如果是這種情況,它也會導致(未說明的)假設 - C擴展對象,並導致說......
class C{ public void doSomethingMEaningful(); }; List< ? extends C > allAtSea = new ArrayList<...>(); List< ? extends Object > allObjects = new ArrayList<...>(); allObjects.add(new Integer(88) ); ... allAtSea.addAll(allObjects); ... allAtSea.get(...).doSomethingMeaningful(...); // Uh-oh.. this finds the Integer 88
class C{ public void doSomethingMEaningful(); }; List< ? extends C > allAtSea = new ArrayList<...>(); List< ? extends Object > allObjects = new ArrayList<...>(); allObjects.add(new Integer(88) ); ... allAtSea.addAll(allObjects); ... allAtSea.get(...).doSomethingMeaningful(...); // Uh-oh.. this finds the Integer 88
class C{ public void doSomethingMEaningful(); }; List< ? extends C > allAtSea = new ArrayList<...>(); List< ? extends Object > allObjects = new ArrayList<...>(); allObjects.add(new Integer(88) ); ... allAtSea.addAll(allObjects); ... allAtSea.get(...).doSomethingMeaningful(...); // Uh-oh.. this finds the Integer 88
class C{ public void doSomethingMEaningful(); }; List< ? extends C > allAtSea = new ArrayList<...>(); List< ? extends Object > allObjects = new ArrayList<...>(); allObjects.add(new Integer(88) ); ... allAtSea.addAll(allObjects); ... allAtSea.get(...).doSomethingMeaningful(...); // Uh-oh.. this finds the Integer 88
C ++ FAQ在21.3提供了一個清晰的例子
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.