2013-05-22 15 views
10

私は完全に(私の.NETの背景で)理解できないJavaのジェネリックスの動作に遭遇しました。Javaのジェネリックで型の安全

public class TestGeneric<T> 
{ 
    public void get (Object arg) 
    { 
     T temp = (T) arg; 

     System.out.println(temp.toString()); 

     return; 
    } 
} 

TestGeneric<Integer> tg = new TestGeneric<Integer>(); 
tg.get("Crack!!!"); 

私が手にClassCastExceptionが届かない理由を、さらに、アイデアに私は割り当てた後StringとしてTEMPを参照して"Crack!!!"の値を有する、教えてください。また、どのようにClassCastExceptionをスローすることができますか? Windows 7 x64でJDK 1.7.0_07を使用しています。

答えて

14

理由あなたならば、一般的なタイプにClass<T>のインスタンスを渡し、その後myClass.isInstance(arg)を行うと、例外をスローすることですtype-erasure

上のリンクありJavaジェネリックが型消去によって実装されているということです。 CLSの大幅な変更が必要な.NETジェネリックとは異なり、Javaジェネリックは完全にコンパイル時に処理されます。実行時に、Tへのキャストは無視されます。実行時に型をチェックするためには、Class<T>を保存し、渡されたパラメータの種類を確認するために、そのメソッドを使用する必要があります。

public class TestGeneric<T> 
{ 
    private Class<T> genClass; 
    public TestGeneric(Class<T> t) {genClass = t;} 
    public void get (Object arg) 
    { 
     if (!genClass.isInstance(arg)) { 
      throw new ClassCastException(); 
     } 
     T temp = (T) arg; 

     System.out.println(temp.toString()); 

     return; 
    } 
} 

TestGeneric<Integer> tg = new TestGeneric<Integer>(Integer.class); 
tg.get("Bang!!!"); // Now that's a real Bang!!! 
+1

あなたは本当に「する必要がありますか」、あるいはより良い「ベストプラクティス」がありますか?私は、nitegazer2003のアプローチが好きです。は、明確に宣言されている/目に見える/簡潔でコード本体を乱雑にしていないためです。また、C#でジェネリックスをどのように制限することができるのかもよく似ています。 –

+0

あなたは彼の質問に正しく答えてはいますが、ジェネリックを正しく使う方法の問題があります。なぜなら、何の最後にも「リターン」が必要なのでしょうか?必要以上に複雑な方法のように見えます。私はジェネリックをどのように使うべきかという答えを加えました。 – Jaxox

3

これはtype-erasureが原因です。つまり、Javaでは、すべてのジェネリックが実行時にObjectに縮小されます。あなたはStringObjectにキャストしました。これは全く問題ありません。 toStringObjectに実装されているため、例外もありません。ここで

は本当にClassCastExceptionを取得するための唯一の方法はfalse

+0

本当に「唯一の方法」ですか? nitegazer2003は、明確な宣言/可視/簡潔でコード本体を乱雑にしないので、より洗練されたアプローチとして私に打撃を与えるものを指摘しました。しかし、「Javaでは、すべてのジェネリックがObjectに縮小されています。 –

+0

私が「唯一の方法」を意味するのは、ジェネリックは実行時に定義された型を持たないということです。したがって、ClassCastExceptionをスローすることが望ましい場合は、タイプを確認するために使用できる 'Class 'のインスタンスを渡すことによって、型をジェネリックに明示的に提供する必要があります。 'Number'に関するあなたのコメントは、それが条件である場合にのみ有効です。ジェネリック実装には、 'Number'のインスタンスに対してのみ機能することを提案するものは何もありません。 –

1

タイプIntegerはメソッドtoStringを持っています。実際にはObjectごとにこの方法がありますのでClassCastExceptionは発生しません。

オブジェクトにString固有のメソッドを呼び出さなかったため、例外は発生しませんでした。

この理由は、実行時にtype erasureの型パラメータの値が表示されなくなるためです。

重要なことは、コードがコンパイルされた後は、それらが消去されているため、ジェネリック型のパラメータを見ることができなくなるということです。

クラスキャスト例外を説明し、ここで別の質問があります。そのコードでexplanation

は、ユーザーがいない一般的なパラメータにStringに明示的にキャストしようとしたことがわかります。

これはC#と比べてJavaの欠点といえます。

+1

-1:何かをキャストするときに 'toString'は自動的に呼び出されません。そして、このキャストはとにかく別の方向です。 –

+1

私はそれが自動的に呼び出されるとは言いませんでした。 –

+0

あなたの答えを編集した後は、toStringをまったく参照していないようですが、これは適切ではありません。私が実際に落としてしまえば、私は今それを撤回するだろうが、私のdownvoteが取らなかったようだ。 –

7

これは、ジェネリック型Tには境界が定義されていないため、オブジェクトとして扱われるからです。この場合、何かをTにキャストしてもClassCastExceptionは発生しません。

ただし、クラス定義がpublic class TestGeneric<T extends Number>の場合、get()にStringを渡すとClassCastExceptionが発生します。

+1

実際にClassCastExceptionを取得する方法に関するOPの質問に答えるために+1。 –

3

常にJavaのGenericsはコンパイル時のエンティティです。実行時には何もしません。あなたにoqnコードをデモさせてください。

public class TestGeneric<T> 
{ 
    public void get (Object arg) 
    { 
     T temp = (T) arg; 

     System.out.println(temp.toString()); 
     System.out.println(temp.getClass()); 

     return; 
    } 

    public static void main(String args[]) 
    { 
     TestGeneric<Integer> tg = new TestGeneric<Integer>(); 
     tg.get("Crack!!!"); 
    } 
} 

と出力が

は今理にかなって

Crack!!! 
class java.lang.String 
のですか? オブジェクトは最高のスーパークラスです。したがって、 多型のためにStringオブジェクトを取得できます。あなたは型キャストではなく、むしろ私は内部でそれが実行時にStringオブジェクトであることを知っている文字列オブジェクトに整数参照ポイントを作ると言うでしょう。 toString()でない場合、整数クラスのと定義されていました。呼び出す関数は と定義されていますが、参照先はですが、実装は ランタイム 参照オブジェクトから適切に取得されます。

6

これは、一般的なコードは「に消去」されていることを非汎用コードがどのように見えるかを考えるための有益な運動です:あなたはこのようにそれを行う場合

public class TestGeneric 
{ 
    public void get (Object arg) 
    { 
     Object temp = (Object) arg; 

     System.out.println(temp.toString()); 

     return; 
    } 
} 

TestGeneric tg = new TestGeneric(); 
tg.get("Crack!!!"); // should there be any problem? 
+0

驚くばかりの答え!消去が実際に行うことの具体例 –

3

、あなたもありませんClassCastExceptionをチェックする必要があり、コンパイルすることさえできません。

public class TestGeneric<T> { 
    public void get (T arg){ 
     System.out.println(arg.toString()); 
    } 
} 

TestGeneric<Integer> tg = new TestGeneric<Integer>(); 
tg.get("Crack!!!"); 
関連する問題