簡體   English   中英

Java泛型編譯器錯誤:不兼容的類型

[英]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所有泛型類型參數,通配符和非通配符)。 在你的情況下, TD<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM