2012-02-16 17 views
11

は、次の点を考慮推測タイプ:ABC:の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?

+0

はTの種類を得ることに、この質問を参照してください - [実行時にクラスのジェネリック型を取得します](のhttp:// stackoverflowの.com/questions/3403909/get-generic-type-of-class-at-runtime) – csturtz

答えて

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); 
} 

の上にシンタックスシュガーであります実行時にはの型としてjava.lang.Objectが使用されます。メソッドのコピーは1つだけです。

他の言語(C#、C++テンプレート、Ada)でのジェネリックスの経験があれば、あなたが知っているものとは対照的ですが、これはカバーの下での動作です。

+0

タイプ消去ではなく、コンパイル可能な汎用の境界についてです。 ' 'を使用すると、出力は' String:abc'になります。 –

+1

@Daniel絶対に - メソッドが 'static void printWithClass(String t)'になるからです。しかし、コンパイル時に静的な 'print'メソッドの単一のオーバーロードを呼び出すためにバインドされた単一のメソッドで、単一のメソッドのままです。 – dasblinkenlight

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

は、コンパイラによって解釈され

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

ジェネリックにcomipled、彼らは任意の実行時のキャストの問題を回避するために追加の型チェックを強制されます。汎用タイプの情報は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