16

私が知らない微妙な何かがここで起こっているに違いないと思います。次の点を考慮してください。

public class Foo<T> {
  private T[] a = (T[]) new Object[5];

  public Foo() {
    // Add some elements to a
  }

  public T[] getA() {
    return a;
  }
}

メイン メソッドに次のものが含まれているとします。

Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();

にキャストできないCastClassExceptionというメッセージが表示されます。java.lang.Objectjava.lang.Double

誰でも理由を教えてもらえますか? 私の理解でClassCastExceptionは、オブジェクトをキャストできない型にキャストしようとするとスローされるということです。つまり、インスタンスではないサブクラスに対してです(ドキュメントを引用するため)。例えば:

Object o = new Double(3.);
Double d = (Double) o; // Working cast
String s = (String) o; // ClassCastException

そして、私はこれを行うことができるようです。がarray ではなくa単なる a の場合、問題なく取得およびキャストできます。配列がこれを壊すのはなぜですか?TT[]a

ありがとう。

4

3 に答える 3

21
Foo<Double> f = new Foo<Double>();

このバージョンのジェネリック クラス Foo を使用すると、メンバー変数aに対して、コンパイラは基本的に次の行を取ります。

private T[] a = (T[]) new Object[5];

これを取得するには、次Tのように置き換えます。Double

private Double[] a = (Double[]) new Object[5];

Object から Double にキャストできないため、ClassCastException が発生します。

更新と明確化:実際には、いくつかのテスト コードを実行した後、ClassCastException はこれよりも微妙です。たとえば、次のメイン メソッドは例外なく正常に動作します。

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    System.out.println(f.getA());
}

f.getA()タイプ の参照に代入しようとすると、問題が発生しますDouble[]

public static void main(String[] args) {
    Foo<Double> f = new Foo<Double>();
    Double[] a2 = f.getA(); // throws ClassCastException
    System.out.println(a2);
}

これは、メンバー変数に関する型情報がa実行時に消去されるためです。ジェネリックは、コンパイル時にのみ型安全性を提供します(最初の投稿では、これをどういうわけか無視していました)。だから問題はそうではない

private T[] a = (T[]) new Object[5];

実行時にこのコードは実際には

private Object[] a = new Object[5];

この問題はgetA()、実行時に実際に を返すメソッド の結果Object[]が型 の参照に割り当てられているDouble[]場合に発生します。オブジェクトを Double にキャストできないため、このステートメントは ClassCastException をスローします。

更新 2 :「配列がこれを壊すのはなぜですか?」という最後の質問に答えるために。その答えは、言語仕様が汎用配列の作成をサポートしていないためです。詳細については、このフォーラムの投稿を参照してください。下位互換性を維持するために、実行時の T の型については何もわかっていません。

于 2008-12-16T18:46:18.417 に答える
5

@mattb の説明には小さな誤りがあるかもしれません。

エラーはありません

java.lang.Object は java.lang.Double にキャストできません。

それは:

[Ljava.lang.Object; [Ljava.lang.Double にキャストできません

[L は配列を意味します。つまり、エラーは、 Object の配列を Double の配列にキャストできないということです。これは、次の場合と同じです。

Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;

これは明らかに許可されていません。

タイプセーフな配列を作成する問題については、init でクラス オブジェクトを除外し、Array.newInstance を使用する別の方法があります。

import java.lang.reflect.Array;

class Foo<T> {
  private T[] a ;

  public Foo(Class<T> tclass) {
    a = (T[]) Array.newInstance(tclass, 5);
  }

  public T[] getA() {
    return a;
  }

  public static <T> Foo<T> create(Class<T> tclass) {
    return new Foo<T>(tclass);
  }
}

class Array1
{
  public static final void main(final String[] args) {
    Foo<Double> f = Foo.create(Double.class);
    Double[] d = f.getA();
  }


}
于 2008-12-17T04:21:45.993 に答える
3

@マットb:答えてくれてありがとう!非常に役立ちます。

興味のある人のための回避策を見つけました。getA メソッドに初期化された配列を渡して入力します。そうすれば、型情報が利用可能になります。

public class Foo<T> {
  private T[] a = (T[]) new Object[5];

  public Foo() {
    // Add some elements to a
  }

  public T[] getA(T[] holdA) {
    // Check whether holdA is null and handle it...then:
    holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length);
    System.arraycopy(a, 0, holdA, 0, a.length);
    return holdA;
  }
}

次に、メインメソッドについて:

public static void main(String[] args) {
  Foo<Double> f = new Foo<Double>();
  Double[] a2 = new Double[1];
  a2 = f.getA(a2);
}
于 2008-12-16T20:04:25.060 に答える