8

これが私が使用しているコードです

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        T[] newArray = (T[]) new Object[5];
        ...
    }

}

タイプセーフではないため、このような配列を作成するのは悪い考えだと人々が言っ​​ているのを見てきました。でも、毎回使ってますが、全然問題ありません。このような配列を作成すると、いつ問題が発生しますか?

ありがとう

4

7 に答える 7

4

問題を引き起こす例を次に示します。

public class Main {

    public static class List<E extends Number> {

        private E[] myE;

        public void hello(E e) {
            E[] newArray = (E[]) new Object[5];
            for (int i = 0; i < newArray.length; i++) {
                newArray[i] = e;
            }
            myE = newArray;
        }

    }

    public static <T> T[] createArray(int size) {
        return (T[]) new Object[size];
    }

    public static void main(String[] args) throws IOException {
        List<Integer> integers = new List<Integer>();
        integers.hello(5);
    }
}

ジェネリック パラメーターを宣言する<T>とバインドされないため、コードが機能します。つまり、拡張されObjectます。配列をキャストする(T[])と、実際にキャストされ(Object[])ます。これは、コンパイラが実行できる最善の方法だからです。配列をコード内に保持していれば、それほど多くの問題は発生しないはずです。しかし、コード外の誰かがその配列を取得でき、オブジェクト以外の型でクラスをインスタンス化した場合、ClassCastException が発生します。

于 2012-04-05T16:12:19.643 に答える
4

Java は実行時に T の型を認識しないため、T の配列を作成することはできません。これは、Java ではジェネリックが型消去で実装されているためです。これは、コンパイラが、すべてが OK であることを確認した後、ほとんどのジェネリック型情報を破棄することを意味します。

配列の場合は話が異なります。Java は特定の配列を作成するために T の正確な型を知る必要があり、そのようなことを決定できないため、ジェネリック型の配列を作成できないためです。

できることは、使用したい実際の配列のインスタンスを提供することであり、Java コンパイラーはそれが適切な型であることを確認できます。

public static <T> void fillWith(T[] destiny, List<? extends T> source){
    for(int i=0; i<= destiny.length; i++){
        destiny[i] = source.get(i);
    }
}

このjava.utils.Arrays.copy方法では、ジェネリックとリフレクションを慎重に使用して、やりたいことの参考として使用できる代替手段を提供します。

public static <T> T[] copyOf(T[] original, int newLength) {
   return (T[]) copyOf(original, newLength, original.getClass());
}


public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
}
于 2012-04-05T16:11:20.150 に答える
2

タイプ セーフではないもの自体は問題を引き起こしません。しかし、適切なタイミングまでポップアップしない問題をコンパイル時に隠すことができます。

タイプ セーフでない環境でも問題なく完全に動作することができますが、タイプ セーフな環境が一般的な実行時エラーのセットがないことを保証するという理由だけで、それは悪い習慣です。現時点では保証はありませんが、これらは非常に重要です。信頼できる安全性は、労力が少なくて済むことを意味します。

于 2012-04-05T15:55:26.843 に答える
1

タイプセーフではないため、このような配列を作成するのは悪い考えだと人々が言っ​​ているのを見てきました。でも、毎回使ってますが、全然問題ありません。このような配列を作成すると、いつ問題が発生しますか?

実行時型がif is notObject[]型であるオブジェクトをキャストすることは論理的に正しくないため、人々はそれは悪い考えだと言います。T[]TObject

Tただし、クラス内 ( のスコープ内)Tが消去されるため、すぐに問題が発生することはありません。したがって、からObject[]へのキャストT[]はチェックされません。

この変数をクラス内 (型 variable のスコープ内T) で使用するだけで、クラス外の人にそれを返すことはなく、クラス外の人がそれにアクセスする方法がない場合は、問題は発生しません。

実際、この方法には利点があります。配列の要素を取得および設定するときに、ジェネリック型チェックのすべての利点が得られます。これは、完全に「型安全」に従うだけでは得られないものです。 type の変数を使用する解決策Object[]。この場合、そこから得たものを手動でキャストする必要があります。

この配列変数を type として外部に返す (または外部からアクセスできるようにする) と、問題が発生します。これT[]は、呼び出し元のコードが特定の型の配列を想定し、ジェネリックが呼び出し元にキャストを挿入するためです。この配列を受け取ったときにコード化され、キャストは実行時に失敗します (たとえば、 aObject[]は a ではなく、String[]実行時に異なる型であるため)。簡単な例を次に示します。

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        elements = (T[]) new Object[5];
        ...
    }

    public T[] getArray() {
        return elements;
    }

}

// some other code...
aClass<String> foo = new aClass<String>(new String[2]);
foo.doSomething();
String[] bar = foo.getArray(); // ClassCastException here
于 2012-04-06T00:37:28.950 に答える
1

この場合は a を使用する方が安全Collectionです。のようなものを作るだけです

...
Collection<T> newElements = new ArrayList<T>(5);
...

とにかく、私が知っていることから、このようなジェネリック配列を作成しても実際の問題は発生しませんが、明示的な型キャストが必要なため、「臭いがします」。この場合、本当に配列が必要ですか?

于 2012-04-05T16:00:56.720 に答える
1

他の人は間違っています。作成時に型のインスタンスがない限り、配列を作成する他の方法はありません。Javaでジェネリック配列を作成する方法も参照してください。

型インスタンス (= 型を持つものClass<T>) がある場合は、呼び出すことができますjava.lang.reflect.Array.newInstance(Class<?>, int)が、それでもキャストが必要になるので、わざわざする必要はありません。

の型が of の代わりにelementsだった場合は状況が異なると思いますが、そうではないため、コードは完全に問題ありません。ObjectT

これをさらに隠したい場合は、ヘルパー メソッドを記述できます。

  @SuppressWarnings( "unchecked" )
  public static <T> T[] createArray( int size ) {
      return (T[]) new Object[size];
  }

これにより、呼び出すときにキャストを必要とせずに配列が作成されます。

于 2012-04-05T15:56:57.887 に答える
0

比較と印刷で問題が発生する可能性があります..基本的に、型を知ることが書式設定にとって重要な場合はいつでも。あなたがそれを持っている方法では、システムは配列にどのデータがあるのか​​ わかりません

于 2012-04-05T15:48:51.480 に答える