2016-09-14 167 views
6

假設我們有以下的通用類:未指定類型參數

public class GenericClass<U> { 
    private U value; 

    public GenericClass(U value) { 
     this.value = value; 
    } 
} 

,並在其他一些MyClass類以下通用方法:

public <T extends BT, BT> void genericMethod(T arg) { 
    Object genericClass = new GenericClass<BT>(arg); 
} 

哪個值BT類型參數獲得如果我們打電話

genericMethod("text"); 

一些注意事項:

上面的代碼編譯沒有錯誤或警告,這對我來說似乎很奇怪。 反編譯(由IntelliJ IDEA的2016的裝置)示出了下面的代碼:

public <T extends BT, BT> void genericMethod(T arg) { 
    new MyClass.GenericClass(arg); 
} 

注意,新GenericClass<BT>(arg)是不一樣的new GenericClass(arg)因爲後者是等效new GenericClass<T>(arg)(型扣)的,並且儘管T extends BT,這些都是不同類型和GenericClass可能具有內部邏輯,其中確切類型名稱發揮重要作用(例如,在某些地圖等中用作字符串鍵)。所以對我來說,很奇怪爲什麼編譯器默默地使用類型推導而不是產生一些警告(或者甚至錯誤),指出BT類型參數沒有被指定。也許我錯過了水手。關於Java泛型重要的,但是......

回答

3

這個答案的第一部分將調用時解決您的問題

唯一的地方,一類是自動在你的例子是推斷出來的類型推斷部分genericMethod

genericMethod的定義聲明瞭兩個通用類型參數:an unbound BTT which should be a subclass of BT

類型T將根據Java language specification section 18.2.4指定的規則進行推斷:

形式,其中S和T是類型的約束公式,減小如下:

...

否則,如果T是推理變量,α和S不是原始類型,則約束減少到邊界S =α。

在您的示例中,您提供了一個String類型的對象作爲類型爲T的參數。 根據上述規則,T將被推斷爲等於String

現在T是固定的,推斷將繼續進行BT。這種類型的由以下section 18.3.1它說,類型的表達式推斷:

T extends BT, T == String

意味着

String extends BT

這種關係允許編譯器結合BTString,因而你結束了以下隱含電話:

genericMethod<String,String>("text") 

希望這可以清理一些東西。

爲了回答你問題的第二部分:

雖然牛逼擴展BT,這些都是不同的類型,以及GenericClass 可能有內部邏輯,其確切的類型名稱起着重要 作用(例如,在某些地圖等中用作字符串鍵)

無需添加更多的約束,編譯器會將T等同於BT,並允許您呼叫只定義爲BT類型的一部分的方法。

除非你使用反射做了非常奇怪的事情,否則代碼不應該依賴於類型參數的運行時值。 現在的GenericClass並不是很有用,因爲U沒有任何約束,所以編譯器將允許你只做對所有對象都有效的東西。 在這種情況下,您可以考慮UObject相同。

+0

謝謝你的回答。 什麼是「推理變量」?你可以在我的代碼示例中指向這樣的嗎?在genericMethod()聲明中,我只看到兩個*類型變量*(T和BT)和一個常用變量(arg)。 我在第18.1.1節中讀到了以下內容:_「推理變量是類型的元變量 - 也就是說,它們是允許抽象推理類型的特殊名稱,爲了將它們與類型變量區分開來,推理變量被表示與希臘字母,主要是α「_。然而,它並沒有爲我清楚這個詞。 –

+0

**你寫:** _string延伸BT 這種關係允許編譯器BT綁定到字符串,所以你結束了以下隱含調用: genericMethod ( 「文本」)_ - 我不明白爲什麼它允許這樣做:「字符串擴展BT」與「BT ==字符串」不一樣! **您也可以編寫:** _如果不添加更多約束,編譯器會將T等同於BT,並允許您僅調用定義爲BT類型一部分的方法._ - 同樣在這裏!編譯器會更好地產生BT在任何地方都不使用的錯誤。 –

+0

**您寫:** _目前的GenericClass並不是非常有用[...]。你可以認爲U在這方面與Object._ 相同 - 我同意:這個樣本是一個太蒸餾的樣本。在我的真實項目中,_JAXBElement_站在這裏(代替GenericClass)。 –

1

基於你的問題,這部分

GenericClass可以具有其中的確切類型名稱中起着重要作用(例如,被用作在一些地圖等的字符串鍵)內部邏輯。

我想可能會有一些混淆,關於如何推斷這些類型。重要的是要認識到,GenericClass將包含的類型是任何你傳入的運行時類型。依賴於確切類型的任何東西,你可能在內部做的事情(儘管通常你不應該這樣做)將工作得很好。

總的來說,編譯時完成的類型檢查可能對你來說看起來有些鬆散(特別是如果你來自C++)。關於這個問題的答案有一些有趣的討論。 Java generics - type erasure - when and what happens