2013-04-08 149 views
25

我學習Java的通用功能,我不知道如何解釋第三條線在以下main方法:調用Java泛型方法

public class Example4 { 
    public static void main(final String[] args) { 
     System.out.println(Util.<String>compare("a", "b")); 
     System.out.println(Util.<String>compare(new String(""), new Long(1))); 
     System.out.println(Util.compare(new String(""), new Long(1))); 
    } 
} 

class Util { 
    public static <T> boolean compare(T t1, T t2) { 
     return t1.equals(t2); 
    } 
} 

第一行編譯,運行和返回(如預期) false

第二行不按預期編譯,因爲我明確混合StringLong

第三行編譯,運行並返回false,但我不確定要了解它是如何工作的:編譯器/ JVM是否實例化參數類型TObject? (另外,有沒有辦法獲得這種聲明類型的T是運行時?)

謝謝。

回答

9

答案似乎超越@Telthien和@newacct的答案。我很好奇, 「看」 爲自己的區別是:用明確地打字

System.out.println(Util.<String>compare("a", "b")); 

,並:

System.out.println(Util.compare(new String(""), new Long(1))); 

與隱式類型。

我進行了幾個實驗,使用前兩行的變化。這些實驗表明,即使在使用anonymous/local class trick時,編譯器在編譯期間也會檢查類型,但生成的字節碼僅指代Object,即使在第一行的情況下。

下面的代碼段示出了類型轉換甚至可以在顯式類型參數<String>的情況下安全地進行一路Object

public final class Example44 { 
    public static void main(final String[] args) { 
     System.out.println(new Util44<String>().compare("a", "b")); 
     System.out.println(new Util44().compare(new String(""), new Long(1))); 
    } 
} 

final class Util44<T> { 
    private T aT; 
    public boolean compare(T t1, T t2) { 
     System.out.println(this.aT); 
     // I was expecting the second and third assignments to fail 
     // with the first invocation because T is explicitly a String 
     // and then to work with the second invocation because I use 
     // a raw type and the compiler must infer a common type for T. 
     // Actually, all these assignments succeed with both invocation. 
     this.aT = (T) new String("z"); 
     this.aT = (T) new Long(0); 
     this.aT = (T) new Object(); 
     return t1.equals(t2); 
    } 
} 

main方法的字節碼的樣子:

// Method descriptor #15 ([Ljava/lang/String;)V 
    // Stack: 7, Locals: 1 
    public static void main(java.lang.String[] args); 
    0 getstatic java.lang.System.out : java.io.PrintStream [16] 
    3 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    6 dup 
    7 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    10 ldc <String "a"> [25] 
    12 ldc <String "b"> [27] 
    14 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    17 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    20 getstatic java.lang.System.out : java.io.PrintStream [16] 
    23 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    26 dup 
    27 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    30 new java.lang.String [39] 
    33 dup 
    34 ldc <String ""> [41] 
    36 invokespecial java.lang.String(java.lang.String) [43] 
    39 new java.lang.Long [46] 
    42 dup 
    43 lconst_1 
    44 invokespecial java.lang.Long(long) [48] 
    47 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    50 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    53 return 
     Line numbers: 
     [pc: 0, line: 24] 
     [pc: 20, line: 25] 
     [pc: 53, line: 26] 
     Local variable table: 
     [pc: 0, pc: 54] local: args index: 0 type: java.lang.String[] 

它實際上是有道理的,所有的調用總是與Object作爲正式的參數類型的方法,如解釋in another question/answer。總而言之,編譯器總是使用Object來生成字節碼,無論是明示類型參數(第一行)還是隱式類型參數都無關緊要,但對象可以具有不同於Object的公共超類。

+1

這裏要注意這一點,即使你鏈接到它;這種現象被稱爲「類型刪除」。 – 2014-11-07 01:50:20

+0

是的。基本上,Java將泛型實現爲幕後的類型轉換。例如,'ArrayList '在內部將每個元素都視爲Object,但在使用時會自動將其轉換回String。 – 2016-02-04 19:50:28

20

共享繼承類型StringLongObject

當您將此函數作爲Util.<String>compare(運行時,編譯器希望找到兩個字符串輸入,並在不存在時給出錯誤。但是,在沒有<String>的情況下運行它會導致使用最接近的共享繼承類型 - 在本例中爲Object

因此,當compare接受t1t2時,它們已被鑄造爲Object,並且代碼運行良好。

要在運行時獲得實際類型,請使用與任何其他對象相同的技巧:從Object類繼承的getClass()

+1

對於第二部分(在運行時獲取聲明類型的T的方式),在Google上搜索「實體泛型」。請參閱gafter.blogspot.com/2006/11/reified-generics-for-java.html – prashant 2013-04-08 09:06:36

+0

應用於t1的getClass()方法將返回String,因爲它是運行時類型,不是嗎? – 2013-04-08 09:19:54

+0

在進行更多研究的同時,我偶然發現了這個有趣的博客文章:[super type tokens](http://gafter.blogspot.kr/2006/12/super-type-tokens.html)。 – 2013-04-08 12:26:37

2

是的,ObjectT的選擇,它將允許它編譯。從概念上講,編譯器推斷類型爲T。它特別推斷的並不重要 - 只要它可以推斷某些類型將爲T工作,那麼它編譯。推斷類型是什麼並不重要,因爲它對編譯後的代碼沒有影響。