2012-02-16 202 views
11

考慮以下幾點:Java泛型 - 類型推斷

public class GenericTest { 
    static void print(int x) { 
     System.out.println("Int: " + x); 
    } 
    static void print(String x) { 
     System.out.println("String: " + x); 
    } 

    static void print(Object x) { 
     System.out.println("Object: " + x); 
    } 

    static <T> void printWithClass(T t) { 
     print(t); 
    } 
    public static void main(String argsp[]) { 
     printWithClass("abc"); 
    } 
} 

它打印對象:ABC。 爲什麼不打印字符串:abc?

+0

看到在得到T的類型這個問題 - [在運行獲取類通用類型](HTTP://計算器.com/questions/3403909/get-generic-of-class-at-runtime) – csturtz 2012-02-16 14:39:06

回答

4

Java支持方法重寫(動態類型綁定),但不是你想要實現的(重載是靜態多態而不是動態的)。

爲了實現你想在Java中實現的目標,你需要雙重調度。

訪客模式應該是你的朋友在這裏。

我寫過你的代碼示例。

public class Test { 

    public static void main(String argsp[]) { 
     PrintTypeImpl typeImpl = new PrintTypeImpl(new StringType(), new IntType(), new ObjectType()); 
     typeImpl.accept(new PrintVisitor()); 
    } 

    static final class PrintVisitor implements TypeVisitor { 
     public void visit(IntType x) { 
      System.out.println("Int: "); 
     } 

     public void visit(StringType x) { 
      System.out.println("String: "); 
     } 

     public void visit(ObjectType x) { 
      System.out.println("Object: "); 
     } 
    } 

    interface TypeVisitor { 
     void visit(IntType i); 

     void visit(StringType str); 

     void visit(ObjectType obj); 
    } 

    interface PrintType { 
     void accept(TypeVisitor visitor); 
    } 

    static class StringType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static class ObjectType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static class IntType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static final class PrintTypeImpl implements PrintType { 

     PrintType[] type; 

     private PrintTypeImpl(PrintType... types) { 
      type = types; 
     } 

     @Override 
     public void accept(TypeVisitor visitor) { 
      for (int i = 0; i < type.length; i++) { 
       type[i].accept(visitor); 
      } 
     } 
    } 

} 
0

因爲java泛型不是您認爲的泛型方式。當通用java代碼被編譯時,所有類型的信息實際上被剝離,只有基本已知類型保留。在這種情況下,該類型是Object

java中的泛型實際上只是編譯器的詭計,編譯器會刪除否則會導致編譯時間受到限制的轉換。最後,剩下的就是基本類型,這實際上被編譯成字節碼。

該過程被稱爲類型擦除。這previous question有助於瞭解實際發生的情況。

0

因爲它只能在運行時才知道,但實際上,因爲java是一種編譯語言而不是腳本語言,所以它正在編譯時決定。

java泛型允許「一種類型或方法操作各種類型的對象,同時提供類型安全的編譯時間」。

當然,你可以嘗試這樣的:

static <T extends String> void printWithClass(T t) { 
    print(t); 
} 

雖然這不是你以後,因爲編譯器發號施令這是不可能的。

10

這是因爲Java type erasure:你

static <T> void printWithClass(T t) { 
    print(t); 
} 

實際上是對

static void printWithClass(Object t) { 
    print(t); 
} 

頂部的語法糖說句公道話,說:「語法糖」讓編譯器做了一些非常好的,重要的檢查,但在運行時只有一個printWithClass方法的副本,它使用java.lang.Object作爲變量的類型t

如果您有其他語言(C#,C++模板,Ada)的泛型經驗與您所知道的相反,但這是它在封面下的工作原理。

+0

這不是關於類型擦除,而是關於在編譯時可用的通用邊界。如果我們使用'',則輸出是'String:abc'。 – 2012-02-16 14:47:08

+1

@Daniel絕對 - 因爲這個方法會變成'static void printWithClass(String t)'。但它仍然是一個單一的方法,只有一種類型,在編譯時綁定來調用靜態'print'方法的一個重載。 – dasblinkenlight 2012-02-16 14:49:53

0
static <T> void printWithClass(T t) { 
    print(t); 
} 

將comipled到

static void printWithClass(Object t) { 
    print(t); 
} 
0

泛型編譯器的解釋和執行這些額外的類型檢查,以避免任何運行時鑄造的問題。通用類型信息是lost at runtime。所以在運行時printWithClass接收的只是對象而不是String,因此也是你的結果。

4

這不是關於類型擦除,它是一個編譯問題,如果JVM在運行時存儲方法泛型,會發生同樣的情況。這也與類型推斷無關 - 編譯器按照您的預期推斷<String>

問題是當編譯器生成代碼爲printWithClass時,它需要一個特定的方法簽名才能與print調用相關聯。 Java沒有多次調度,所以它不能在方法表中放置模糊的簽名並決定在運行時調用什麼。 T唯一的上限是Object,所以匹配的唯一方法是print(Object)

0

額外例子來闡明:

public class OverloadingWithGenerics { 

    static void print(Integer x) { 
     System.out.println("Integer: " + x); 
    } 

    static void print(Double x) { 
     System.out.println("Double: " + x); 
    } 

    static void print(Number x) { 
     System.out.println("Number: " + x); 
    } 

    static <T extends Number> void printWithClass(T t) { 
     print(t); 
    } 

    public static void main(String argsp[]) { 
     printWithClass(new Integer(1234)); 
    } 
} 

此打印:

Number: 1234