2017-07-23 24 views
-1

我明白了嗎?擴展..和?超..對自己和泛型類型是可能的,但我只是不明白下面是如何可能的這個層次的工作:結合<?擴展ClassE>和<?超級ClassB>

- >表示延伸

類是X(最低),A到E(最高)

接口是F

X - > A(實施F) - >乙 - 「ç - > E(實現F)

也d - >電子

public class Node<T extends ClassE> { 
    private T info; 

    public T getInfo() { 
     return info; 
    } 

    public void setInfo(T info) { 
     this.info = info; 
    } 
} 

public static void main (String [] args){ 

    Node<? super ClassB> n2 = new Node<ClassC>(); 
// this makes sense, since Node accepts below E and above B 

    InterfaceF i2 = n2.getInfo(); 
// how? Not only outside of <? extends E> but also getting value even though 
// <? super B> is defined above, what's up with PECS? 

    n2.setInfo(new ClassX()); 
// also.. how? I'm setting a class that's out of the allowed range + 
// seemingly violating the PECS for <? extends E> 
} 

正如你所看到的,當我們將它們組合起來時,我感到非常困惑,而讓我感到驚訝的是讓這些聲明通過編譯器沒有問題。 我在某處讀過兩種邊界的組合在Java中都是不可能的,但是那又如何呢?

+0

你確定這個'Node <?超級ClassB> n2 =新節點();'編譯?參考n2需要參數類型爲B或更高的'Node',而您試圖在繼承層次上傳遞比B低的B. – matoni

+0

我沒有指定任何值來測試它,但我完全能夠執行程序,我100%確定 – mxOx2kL

+0

關於層次結構,E是最高的,X是最低的,對於混淆抱歉。 所以A擴展B,B擴展C,C擴展E – mxOx2kL

回答

0

聲明:我不知道具體的類型推理規則,但解釋我最好的理解。

關於InterfaceF i2 = n2.getInfo() - 自Node<T extends E>確定Node.getInfo()返回extends E。根據你的描述E implements F。因此確保T extends E也將implement F。因此Node.getInfo() = T extends E implements F。所以n2.getInfo() implements F很好。

關於n2.setInfo(new ClassX()) - 我沒有爲如上所述,這正式般的解釋,但讓我們試着想一想:基本上,你Node<? super ClassB>告訴大家期望最多ClassB作爲最低爲Node的內容。但是,由於ClassX可以繼承ClassB,所以它完全有效,因爲它將滿足? super ClassB所提供的所有接口保證。

希望這會有所幫助!

+0

這是我嘗試另一個例子的方法,它太棒了。 這是特別有用的,因爲它就像一步一步,並容易適用於其他情況。 – mxOx2kL

0
public class Test { 
    public interface F {} 
    public static class E implements F {} 
    public static class D extends E {} 
    public static class C extends E {} 
    public static class B extends C {} 
    public static class A extends B implements F {} 
    public static class X extends A {} 

    public static class Node<T extends E> { 
     private T info; 
     public T getInfo() {return info;} 
     public void setInfo(T info) {this.info = info;} 
    } 

    public static void main(String[] args) { 
     Node<? super B> n = new Node<C>(); // C is superclass of B = OK 
     F i = n.getInfo(); // node type = B|C|E all these types implements F (since E implements F) = OK 
     n.setInfo(new X()); // X has supertypes A,B,C,E = can be casted to B and so satisfy <? super B> 
    } 
} 
+0

你也可以解釋你做了什麼或改變了什麼?解釋通常比僅有代碼的答案更有幫助。 – Zabuza

+0

@Zabuza上面所有主要的代碼都遵循OP的繼承,主要的方法有很好的解釋了什麼是正在進行的註釋,那麼爲什麼非代碼行呢? – matoni

+0

堅實的回答和直截了當的要求,我花了一些時間才弄清楚,每一個獲得操作都需要和上限本身,每一個設置操作都需要一個下限。 結合你的解釋我猜一切都變得清晰了。 – mxOx2kL

2

編譯第一行InterfaceF i2 = n2.getInfo();是因爲下界的通配符仍然保留了類型變量本身的界限。由於類型變量具有上限ClassE,所以getInfo()仍然返回ClassE。由於ClassE執行InterfaceF,分配編譯。

換句話說,我們可以想象,當你做Node<? super ClassB>時,你實際上做了一些類似(構成語法)Node<? extends ClassE & super ClassB>Node的類型參數是一個超類型ClassBClassE的子類型。

這與Node<?>Node<? extends ClassE>隱含的相同。

這實際上指定的方式有點複雜,但它在capture conversion。捕獲轉換是編譯器採用帶有通配符的類型並將其視爲不帶通配符的類型的過程,以確定子類型。

G名稱與ň類型參數A1,...,An與相應的邊界U1,...,Un泛型類型聲明。

存在一個捕獲轉換從參數化類型G<T1,...,Tn>到參數化類型G<S1,...,Sn,其中,對於1≤我&le; N

  • [...]

  • 如果Ti是形式爲? super Bi的通配符類型自變量,則Si是新鮮類型變量,其上限爲Ui[A1:=S1,...,An:=Sn],其下限b ound是Bi

換句話說,Si(對應於? super ClassB捕獲轉換後的類型參數)獲得其下界從結合的通配符,並從結合的類型變量聲明的其上限。

第二行n2.setInfo(new ClassX());編譯,因爲ClassXClassB的子類,所以它可以隱式轉換爲它。我們可以想像,n2Node<ClassB>,爲什麼這行編譯它可能會更明顯:

Node<ClassB> n2 = ...; 
n2.setInfo(new ClassX()); 

setInfo接受ClassB以及對ClassB任何亞型。

而且,相對於這一點:

我讀的地方,兩者界限的組合是不可能在Java中,但怎麼才能做到呢?

編譯器中的類型系統做了很多事情,我們不能明確地做自己。另一個很好的例子(儘管無關)是類型推斷無名類:

int num = Objects.requireNonNull(new Object() {int num = 42;}).num; 
System.out.println(num); // 42 

它編譯,因爲類型推斷允許推斷類型參數的requireNonNullT是匿名的對象類型,即使我們永遠無法提供該類型是我們自己的顯式類型參數。

+0

謝謝您花時間深入解釋。 我做了一些測試,問題是我沒有看到方法的類型。由於讀取方法(如getInfo)需要我的類型T的上限,因此n2.getInfo()只能爲E或更低類型。 因此,我可以採取任何以上的東西,如 對象i2 = n2.getInfo();它會工作 類似於setInfo()這是一個寫操作,我需要下限。 另外,你是什麼意思的「匿名對象類型」?你的意思是像對象$ 1這樣的匿名類,它們有自己的類型嗎? – mxOx2kL

+0

*「你的意思是像Object $ 1這樣的匿名類有它們自己的類型嗎?」是的,當你編寫一個像new Object(){}這樣的匿名對象時,你隱式聲明瞭一個新類,但是匿名類有沒有名字,所以沒有辦法引用它。在我的例子中,除了進行什麼類型的推理外,沒有辦法訪問匿名對象之外的'num'。 (我的例子不是任何人都應該使用的代碼,它只是一個奇怪的編譯器怪癖的例子。) – Radiodef

+0

好吧,現在我明白了。我認爲可能會有一些我沒有看到的好處(除了來自匿名類的明顯的)。 非常有趣的例子。 – mxOx2kL

相關問題