Javaが私たちに許可しない理由は何ですか
private T[] elements = new T[initialCapacity];
.NETではそれを行うことができなかったことを理解できました..NETではランタイムに異なるサイズを持つことができる値型がありますが、Javaではすべての種類のTがオブジェクト参照になるため、同じサイズになります(私が間違っている場合は修正してください)。
理由は何ですか?
Javaが私たちに許可しない理由は何ですか
private T[] elements = new T[initialCapacity];
.NETではそれを行うことができなかったことを理解できました..NETではランタイムに異なるサイズを持つことができる値型がありますが、Javaではすべての種類のTがオブジェクト参照になるため、同じサイズになります(私が間違っている場合は修正してください)。
理由は何ですか?
回答:
これは、Javaの配列(ジェネリックとは異なります)に、実行時にそのコンポーネントタイプに関する情報が含まれているためです。したがって、配列を作成するときは、コンポーネントのタイプを知っている必要があります。T
実行時の内容がわからないため、配列を作成できません。
ArrayList <SomeType>
それを行いますか?
new ArrayList<SomeType>()
?ジェネリック型には、実行時に型パラメーターが含まれません。typeパラメータは作成では使用されません。生成されたコードには違いありませんnew ArrayList<SomeType>()
か、new ArrayList<String>()
またはnew ArrayList()
すべてでは。
ArrayList<T>
機能するかについてもっと質問していましたprivate T[] myArray
。コードのどこかに、ジェネリック型Tの配列がなければならないので、どうやって?
T[]
。ランタイムタイプの配列がObject[]
あり、1)ソースコードに次の変数が含まれているObject[]
(これが最新のOracle Javaソースでの方法です)。または2)ソースコードにの型の変数が含まれていますT[]
。これは嘘ですがT
、クラスのスコープ内で消去されるため問題は発生しません。
見積もり:
ジェネリック型の配列は適切ではないため、許可されていません。この問題は、静的には有効ではないが動的にチェックされるJava配列と、静的には有効で動的にチェックされないジェネリックとの相互作用によるものです。次に、抜け穴を悪用する方法を示します。
class Box<T> { final T x; Box(T x) { this.x = x; } } class Loophole { public static void main(String[] args) { Box<String>[] bsa = new Box<String>[3]; Object[] oa = bsa; oa[0] = new Box<Integer>(3); // error not caught by array store check String s = bsa[0].x; // BOOM! } }
Tigerでは拒否された静的に安全な配列(別名Variance)を使用してこの問題を解決することを提案しました。
- ガフター
(私はそれがニール・ガフターだと信じていますが、わかりません)
こちらのコンテキストで確認してください。http://forums.sun.com/thread.jspa?threadID = 457033&forumID = 316
new T()
。Javaの各配列は、設計上、その中にコンポーネントタイプ(つまりT.class
)を格納します。したがって、そのような配列を作成するには、実行時にTのクラスが必要です。
new Box<?>[n]
例では役に立たないかもしれませんが、それでも時々十分かもしれないを使用することができます。
Box<String>[] bsa = new Box<String>[3];
何か変更はありましたか?
きちんとした解決策を提供することに失敗することによって、あなたはただより悪い私見で終わるだけです。
一般的な回避策は次のとおりです。
T[] ts = new T[n];
に置き換えられます(TがObjectを拡張し、別のクラスを拡張しないと仮定)
T[] ts = (T[]) new Object[n];
私は最初の例を好みますが、より多くの学問のタイプは2番目を好むようです、または単にそれについて考えたくないです。
Object []を単に使用できない理由のほとんどの例は、リストまたはコレクション(サポートされている)に等しく適用できるため、非常に貧弱な引数と見なします。
注:これは、コレクションライブラリ自体が警告なしにコンパイルされない理由の1つです。このユースケースを警告なしにサポートできない場合、ジェネリックモデルIMHOで何かが根本的に壊れています。
String[]
ユーザーに返す場合(またはtypeのパブリックにアクセス可能なフィールドに格納し、T[]
誰かがそれを取得する場合)、それらはClassCastExceptionを取得します。
T[] ts = (T[]) new Object[n];
:悪い考えですstackoverflow.com/questions/21577493/...
T[] ts = new T[n];
です。私の元の理由から変わった唯一のことは、彼が言ったことが有効な例であると思ったということです。彼の答えは他の開発者に問題や混乱を引き起こし、話題から外れるため、私は投票を続けます。また、これについてのコメントはやめます。
配列は共変です
配列は共変であると言われます。これは基本的に、Javaのサブタイプ規則が与えられた
T[]
場合、型の配列には型の要素T
またはの任意のサブタイプが含まれる可能性があることを意味しT
ます。例えば
Number[] numbers = new Number[3];
numbers[0] = newInteger(10);
numbers[1] = newDouble(3.14);
numbers[2] = newByte(0);
それだけでなく、Javaのサブタイプ規則で
S[]
は、がのサブタイプであるT[]
場合、配列は配列S
のサブタイプであるT
と明記されているため、次のようなものも有効です。
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
Javaのサブタイプ規則に従って、配列
Integer[]
は配列のサブタイプであるためNumber[]
IntegerはNumberのサブタイプであるためためです。しかし、このサブタイプ規則は興味深い質問につながる可能性があります。これを行おうとするとどうなるでしょうか。
myNumber[0] = 3.14; //attempt of heap pollution
この最後の行は問題なくコンパイルされますが、このコードを実行すると、 ArrayStoreException
すると、整数配列にdoubleを入れようとしているのでます。ここでは、数値参照を通じて配列にアクセスしているという事実は関係ありません。重要なのは、配列が整数の配列であることです。
つまり、コンパイラをだますことができますが、ランタイム型システムをだますことはできません。これは、配列がreifiable型と呼ばれるものだからです。つまり、実行時にJavaは、この配列が実際には整数の配列としてインスタンス化され、たまたまタイプの参照を介してアクセスされることを知っています。Number[]
。
ご覧のとおり、1つはオブジェクトの実際のタイプ、もう1つはオブジェクトへのアクセスに使用する参照のタイプです。
Java Genericsの問題
ここで、Javaのジェネリック型の問題は、型パラメーターの型情報がコードのコンパイルが完了した後にコンパイラーによって破棄されることです。したがって、このタイプ情報は実行時には利用できません。このプロセスはタイプ消去と呼ばれます。このようなジェネリックをJavaで実装することには十分な理由がありますが、それは長い話であり、既存のコードとのバイナリ互換性に関係しています。
ここで重要な点は、実行時にはタイプ情報がないため、ヒープ汚染をコミットしていないことを確認する方法がないということです。
次の安全でないコードを考えてみましょう:
List<Integer> myInts = newArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap polution
Javaコンパイラーがこれをやめない場合、ランタイム型システムも私たちを止めることはできません。これは、実行時に、このリストが整数のみのリストであるはずであると判断する方法がないためです。Javaランタイムでは、整数のみを含める必要がある場合、このリストに何でも入れることができます。これは、作成時に整数のリストとして宣言されていたためです。行番号4は安全ではなく、許可されていると型システムの前提を破る可能性があるため、コンパイラが行番号4を拒否するのはそのためです。
そのため、Javaの設計者は、コンパイラーをだまさないようにしました。(配列の場合のように)コンパイラーをだますことができない場合、ランタイム型システムもだますことができません。
そのため、実行時にジェネリック型の真の性質を判別できないため、ジェネリック型は変更不可能であると言います。
この回答の一部をスキップしました。https: //dzone.com/articles/covariance-and-contravariance
これが不可能である理由は、JavaがそのGenericsを純粋にコンパイラレベルで実装し、クラスごとに生成されるクラスファイルが1つしかないためです。これはType Erasureと呼ばれます。
実行時に、コンパイルされたクラスは同じバイトコードですべての使用を処理する必要があります。したがって、new T[capacity]
インスタンス化する必要があるタイプはまったくわかりません。
答えはすでに与えられていますが、すでにTのインスタンスがある場合は、これを行うことができます。
T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);
希望、私は助けることができる、Ferdi265
T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;
。
T[] t
、それはそうなります(T[]) Array.newInstance(t.getClass().getComponentType(), length);
。私は理解するためにいくつかの時間を費やしましたgetComponentType()
。これが他の人を助けることを願っています。
t.clone()
は返されませんT[]
。そのためt
、この答えでは配列ではありません。
String[]
しObject
て格納できますInteger
。そのArrayStoreException
ため、コンパイル時に問題を検出できなかったため、配列ストア()のランタイム型チェックを追加する必要がありました。(そうでない場合、Integer
実際にはでスタックしている可能性がString[]
あり、それを取得しようとするとエラーが発生し、それは恐ろしいことです。)...
Object
あっObject[]
たはずです。
私はGafterから間接的に与えられた答えが好きです。しかし、私はそれが間違っていることを提案します。Gafterのコードを少し変更しました。コンパイルしてしばらく実行してから、Gafterが予測する場所に爆弾を仕掛けます
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static <T> T[] array(final T... values) {
return (values);
}
public static void main(String[] args) {
Box<String> a = new Box("Hello");
Box<String> b = new Box("World");
Box<String> c = new Box("!!!!!!!!!!!");
Box<String>[] bsa = array(a, b, c);
System.out.println("I created an array of generics.");
Object[] oa = bsa;
oa[0] = new Box<Integer>(3);
System.out.println("error not caught by array store check");
try {
String s = bsa[0].x;
} catch (ClassCastException cause) {
System.out.println("BOOM!");
cause.printStackTrace();
}
}
}
出力は
I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Loophole.main(Box.java:26)
したがって、Javaでジェネリック配列型を作成できるように見えます。質問を誤解しましたか?
私はここでのパーティーに少し遅れていることを知っていますが、これらの回答のどれも私の問題を解決しなかったので、将来のGoogle社員を助けることができると思いました。しかし、Ferdi265の回答は非常に役立ちました。
自分のリンクリストを作成しようとしているので、次のコードがうまくいきました。
package myList;
import java.lang.reflect.Array;
public class MyList<TYPE> {
private Node<TYPE> header = null;
public void clear() { header = null; }
public void add(TYPE t) { header = new Node<TYPE>(t,header); }
public TYPE get(int position) { return getNode(position).getObject(); }
@SuppressWarnings("unchecked")
public TYPE[] toArray() {
TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());
for(int i=0 ; i<size() ; i++) result[i] = get(i);
return result;
}
public int size(){
int i = 0;
Node<TYPE> current = header;
while(current != null) {
current = current.getNext();
i++;
}
return i;
}
toArray()メソッドには、ジェネリック型の配列を作成する方法があります。
TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());
私の場合、次のようなスタックの配列が必要でした:
Stack<SomeType>[] stacks = new Stack<SomeType>[2];
これは不可能だったので、回避策として以下を使用しました。
醜いですが、Javaは満足しています。
注:質問へのコメントでBrainSlugs83によって言及されているように、.NETでジェネリックの配列を持つことは完全に可能です
パラメーター化された型の配列を作成することはできません。たとえば、次のコードはコンパイルされません。
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
次のコードは、さまざまな型が配列に挿入されたときに何が起こるかを示しています。
Object[] strings = new String[2]; strings[0] = "hi"; // OK strings[1] = 100; // An ArrayStoreException is thrown.
一般的なリストで同じことを試みると、問題が発生します。
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed stringLists[0] = new ArrayList<String>(); // OK stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown, // but the runtime can't detect it.
パラメータ化されたリストの配列が許可されている場合、前のコードは目的のArrayStoreExceptionをスローできませんでした。
私には、それは非常に弱いように聞こえます。ジェネリックスを十分に理解している人なら誰でも完全にうまくいき、そのような場合にはArrayStoredExceptionがスローされないことを期待しています。
確かにそれを回避する良い方法があるはずです(おそらくリフレクションを使用します)ArrayList.toArray(T[] a)
。私は引用します:
public <T> T[] toArray(T[] a)
このリストのすべての要素を正しい順序で含む配列を返します。返される配列の実行時の型は、指定された配列の実行時の型です。リストが指定された配列に収まる場合は、そこに返されます。それ以外の場合、新しい配列は、指定された配列の実行時の型とこのリストのサイズで割り当てられます。
したがって、1つの方法は、この関数を使用することです。つまりArrayList
、配列に必要なオブジェクトのtoArray(T[] a)
を作成してから、を使用して実際の配列を作成します。速くはありませんが、要件については言及していません。
それで、誰がどのようtoArray(T[] a)
に実装されているか知っていますか?
Array.newInstance()
。コンパイル時に不明なタイプの配列を作成する方法を尋ねる多くの質問で言及されていることがわかります。しかし、OPは、なぜnew T[]
構文を使用できないのかを具体的に尋ねていました。これは別の質問です
ジェネリック配列をインスタンス化できない場合、言語にジェネリック配列型があるのはなぜですか?オブジェクトのない型を持つことの意味は何ですか?
私が考えることができる唯一の理由は、可変引数- foo(T...)
です。さもなければ、それらは完全に一般的な配列型をスクラブしたかもしれません。(まあ、varargsは1.5より前には存在しなかったため、varargsに配列を使用する必要はありませんでした。これはおそらく別の間違いです。)
だからそれは嘘です、varargsを介してジェネリック配列をインスタンス化できます!
もちろん、一般的な配列の問題はまだ現実です。
static <T> T[] foo(T... args){
return args;
}
static <T> T[] foo2(T a1, T a2){
return foo(a1, a2);
}
public static void main(String[] args){
String[] x2 = foo2("a", "b"); // heap pollution!
}
この例を使用して、ジェネリック配列の危険性を実際に示すことができます。
一方、私たちは10年間汎用の可変引数を使用しており、空はまだ落ちていません。したがって、問題は誇張されていると主張できます。それは大したことじゃないよ。明示的なジェネリック配列の作成が許可されている場合、あちこちにバグがあります。しかし、私たちは消去の問題に慣れており、私たちはそれとともに生きることができます。
そしてfoo2
、私たちは、仕様が私たちを遠ざけると主張する問題から私たちを遠ざけるという主張に反論するように指摘することができます。Sunが1.5のためにより多くの時間とリソースを持っていれば、彼らはより満足のいく解決策に到達できたはずだと思います。
他の人がすでに述べたように、もちろんいくつかのトリックを介して作成できます。
ただし、お勧めしません。
なぜなら型消去、より重要なのはcovariance
、単にサブタイプの配列を可能にする配列では、実行時原因となる値の背中を取得しようとするときに、明示的な型キャストを使用するために強制的スーパータイプの列に割り当てることができるClassCastException
主要な目的の一つでありますジェネリックが排除しようとすること:コンパイル時のより強力な型チェック。
Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException
より直接的な例は、Effective Java:Item 25にあります。
共分散:SがTのサブタイプである場合、タイプS []の配列はT []のサブタイプ
クラスがパラメーター化された型として使用する場合、T []型の配列を宣言できますが、そのような配列を直接インスタンス化することはできません。代わりに、一般的なアプローチは、次に示すように、Object []型の配列をインスタンス化し、T []型にナローイングキャストを行うことです。
public class Portfolio<T> {
T[] data;
public Portfolio(int capacity) {
data = new T[capacity]; // illegal; compiler error
data = (T[]) new Object[capacity]; // legal, but compiler warning
}
public T get(int index) { return data[index]; }
public void set(int index, T element) { data[index] = element; }
}
これを試して:
List<?>[] arrayOfLists = new List<?>[4];