2
import java.lang.reflect.Array;

public class PrimitiveArrayGeneric {
    static <T> T[] genericArrayNewInstance(Class<T> componentType) {
        return (T[]) Array.newInstance(componentType, 0);
    }

    public static void main(String args[]) {
        int[] intArray;
        Integer[] integerArray;

        intArray = (int[]) Array.newInstance(int.class, 0);
        // Okay!

        integerArray = genericArrayNewInstance(Integer.class);
        // Okay!

        intArray = genericArrayNewInstance(int.class);
        // Compile time error:
           // cannot convert from Integer[] to int[]

        integerArray = genericArrayNewInstance(int.class);
        // Run time error:
           // ClassCastException: [I cannot be cast to [Ljava.lang.Object;
    }    
}

Java でジェネリックがどのように機能するかを完全に理解しようとしています。上記のスニペットの 3 番目の割り当てでは、少し奇妙になります。コンパイラは、Integer[]に変換できないと不平を言っていますint[]もちろん、ステートメントは 100% 真実ですが、なぜコンパイラがこの苦情を申し立てているのか疑問に思っています。

その行にコメントを付けて、4 番目の課題のようにコンパイラの「提案」に従うと、コンパイラは実際に満足します!!! これで、コードは正常にコンパイルされます! もちろん、実行時の動作が示唆するように、int[]に変換できないため、これはクレイジーですObject[](これはT[]実行時に型が消去されるものです)。

だから私の質問は、コンパイラInteger[]が3番目の割り当ての代わりに割り当てることを「提案」するのはなぜですか? コンパイラはどのようにしてその (誤った!) 結論に達したのでしょうか?


これまでのところ、2 つの回答には多くの混乱があるため、ここで根本的な問題を説明するために別の不可解な例を作成しました。

public class PrimitiveClassGeneric {    
    static <T extends Number> T test(Class<T> c) {
        System.out.println(c.getName() + " extends " + c.getSuperclass());
        return (T) null;
    }
    public static void main(String args[]) {
        test(Integer.class);
        // "java.lang.Integer extends class java.lang.Number"

        test(int.class);
        // "int extends null"
    }
}

コンパイラが上記のコードをコンパイルできるのは絶対におかしいと思うのは私だけですか?

c.getSuperclass().getName()たとえば、上記のコードで印刷することは不合理ではありませんT extends Number。もちろん、今はwhenをgetName()スローします。NullPointerExceptionc == int.classc.getSuperclass() == null

そして私にとって、それはそもそもコードのコンパイルを拒否する十分な理由です。


おそらく究極の狂気:

    int.class.cast(null);

そのコードはコンパイルされ、正常に実行されます。

4

3 に答える 3

4

の型int.classClass<Integer>であるため、genericArrayNewInstance()を返すと推測されInteger[]ます。しかし、関数は実際には を作成するint[]ため、返されるときにクラス キャスト例外が発生します。T[]基本的に、この場合、関数内へのキャストは正当ではありません。なぜなら、それint[]は a ではないからですT[](プリミティブは型変数では使用できません)。プリミティブ配列型をジェネリックに処理することはできません。そのため、メソッドに type を返すだけにするかObject、参照型とプリミティブ型に対して別々のメソッドを作成する必要があります。

于 2010-03-16T10:17:08.610 に答える
3

いくつかのポイント:

  1. プリミティブは、必要に応じて対応するオブジェクト(ラッパー)に自動ボックス化されます
  2. プリミティブ配列はオブジェクトであるため、自動ボックス化されません。
  3. ジェネリックスは、プリミティブを型パラメーターとして使用できません

あなたの例のために、ここに私の仮定があります:

3では、オートボクシングは型パラメーターに発生しますが、返された配列
には発生しません。4では、自動ボクシングは型パラメーターに発生しますが、メソッド引数には発生しません。したがって、実際にint[] はが生成されますが、Integer[]期待されます。

タイプパラメータの場合のオートボクシングは、正確にはオートボクシングではないかもしれませんが、同じ考えを持つものです。

更新: 2番目の例には何も問題はありません。int.classはであるClassため、コンパイラはそれを拒否する理由がありません。

于 2010-03-16T10:53:39.597 に答える
0

元のポスターに同意します。狂ってる。プリミティブをジェネリックで使用できないのはなぜですか? これはコンパイラの問題ではないかもしれませんが、言語の問題です。ジェネリックからプリミティブ型をスキップするのは単に間違っています。

このため:

intArray = (int[]) Array.newInstance(int.class, 0);

int.class は単なる Class オブジェクトです。だから、渡しても大丈夫。「int」は型なので、明らかにプリミティブなのでダメです。それが言語を作成するための「最良の」方法であると言っているのではなく、言語に固執するだけです。

これは非常にクレイジーなので、ジェネリックを使用してプリミティブのメモリ (配列) 割り当て用のラッパーを作成することはできません。オブジェクトを使用する場合、膨大なコレクションが無駄になりすぎて肥大化します。Java 言語/マシンを作成した人々は、明らかに脳に少し限界があります。彼らは最初にそれを間違って行うことができますが、それを修正するには10年かかり、正しく行うことはできません.

于 2010-12-10T14:45:10.323 に答える