2014-10-11 59 views
4

無法理解Java中的泛型編程。通用方法和通用類中的類型推斷

我看了一些關於它的教程,但仍然很困惑,尤其是當事情變得複雜時。

任何人都可以解釋這個例子中發生了什麼?

import java.util.Date; 

public class Test1 { 

    public static void main(String[] args) { 
     P<Cls> p = new P<>(); //<1> //I expect a ClassCastException here, but no. Why? //How does the type inference for class P<E> work? 
     System.out.println(p.name); //it prints 
//  System.out.println(p.name.getClass());//but this line throws ClassCastException //why here? why not line <1>? 

     test1(p);//it runs 
//  test2(p);//throws ClassCastException//What is going on in method test1&test2? 
       //How does the type inference for generic methods work in this case? 
     } 


    public static<T> void test1(P<? extends T> k){ 
     System.out.println(k.name.getClass()); 
    } 

    public static<T extends Cls> void test2(P<? extends T> k){ 
     System.out.println(k.name.getClass()); 
    } 
} 

class P<E>{ 
    E name = (E)new Date();//<2> 
} 

class Cls{} 

回答

2
P<Cls> p = new P<>(); 

記住的Java由擦除,這意味着P構造並沒有真正有任何想法E在運行時實現仿製藥。 Java中的泛型純粹是爲了在編譯時幫助開發人員。

這意味着,當你創建一個new P<>(),一個new Date()被創建,但實際上並沒有轉換爲任何特定類型的,因爲運行時不知道E什麼。 E在運行時不存在,就類​​而言。 name只是一個Object參考,裏面有一個Date。但是,只要運行時環境需要知道它是特定類型(在本例中爲Cls),編寫代碼時使用name時,編譯器會在不告訴您的情況下將該類型轉換爲該類型。

  • p.name.getClass()被編譯爲((Cls)p.name).getClass(),這將創建一個類轉換異常。
  • test2()指定了非通用類型約束(extends Cls)。所以其致電p.name.getClass()同樣被翻譯爲((Cls)p.name).getClass()

在另一方面:

  • System.out.println(p.name)實際上是相同的,因爲System.out.println((Object)p.name)println一個非通用方法,它有一個object
  • test1(p.name)與此類似。由於運行時間實際上並不知道T是什麼類型,因此在調用getClass()之前,它基本上將p.name作爲Object

換句話說,這裏是你的代碼,因爲它實際上是被編譯:

class P{ 
    Object name = new Date(); 
} 

public static void main(String[] args) { 
    P p = new P(); 
    System.out.println(p.name); 
    System.out.println(((Cls)p.name).getClass()); 

    test1(p); 
    test2(p); 
} 


public static void test1(P k){ 
    System.out.println(k.name.getClass()); 
} 

public static void test2(P k){ 
    System.out.println(((Cls)k.name).getClass()); 
} 
+0

這說明*它發生時*,但它並不能解釋爲什麼* *。爲什麼'p.name.getClass()'涉及到一個強制轉換,而不是'p.name'?即爲什麼不把第一行編譯爲'System.out.println((Cls)p.name);'? – 2014-10-11 12:47:06

+0

@OliverCharlesworth:因爲Java希望運行時知道編譯器知道該對象的什麼,以便調用它的方法。在這種情況下,編譯器繪製的筆畫比所需的更寬,因爲'getClass()'恰好在所有'Objects'上都可用,但是如果它是在'Cls'上聲明的方法,那麼類轉換就是必需的。另一方面,將一個對象傳遞給一個需要'Object'的方法不需要投射。 – StriplingWarrior 2014-10-11 12:56:42

+0

好的,我們所說的確實是編譯器在某些情況下不依賴於上下文而使用cast。問題是,管理這個的規則是什麼?想必它不是隨機的。 (這是一個真正的問題;我無法弄清楚JLS中可能指定的地方等) – 2014-10-11 13:02:56