1) ジェネリックと可変引数に関する特定の問題について、インターネットと StackOverflow に多くの例があります。基本的に、型パラメータ型の可変数の引数がある場合です。
<T> void foo(T... args);
Java では、varargs はコンパイル時に単純な「書き直し」を受けるシンタックス シュガーです。type の varargs パラメータは typeX...
のパラメータに変換されますX[]
。この varargs メソッドが呼び出されるたびに、コンパイラは varargs パラメータに含まれるすべての「可変引数」を収集し、 と同じように配列を作成しますnew X[] { ...(arguments go here)... }
。
これは、varargs 型が のように具体的である場合にうまく機能しますString...
。のような型変数の場合、 がその呼び出しの具象型であることがわかってT...
いる場合にも機能します。T
たとえば、上記のメソッドが class の一部でありFoo<T>
、参照がある場合、コードのその時点であることがわかっているためFoo<String>
、それを呼び出すことは問題ありません。foo
T
String
T
ただし、の「値」が別の型パラメーターの場合は機能しません。Java では、型パラメータ コンポーネント型 ( new T[] { ... }
) の配列を作成することはできません。そのため、Java は代わりにnew Object[] { ... }
(ここObject
に の上限をT
示します。上限が何か異なる場合は、代わりにObject
) を使用し、コンパイラの警告を表示します。
new Object[]
では、代わりに何かを作成することの何が問題なのnew T[]
ですか? Java の配列は、実行時にコンポーネントの型を認識します。したがって、渡された配列オブジェクトは、実行時に間違ったコンポーネント タイプになります。
おそらく最も一般的な varargs の使用法では、単純に要素を反復処理するため、これは問題ありません (配列の実行時の型は気にしません)。したがって、これは安全です。
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
ただし、渡された配列のランタイム コンポーネント タイプに依存するものは安全ではありません。以下は、安全でなくクラッシュする単純な例です。
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
ここでの問題は、 をとして返すためにargs
、の型に依存していることです。しかし実際には、実行時の引数の型は のインスタンスではありません。T[]
T[]
T[]
3) メソッドに型の引数がある場合T...
(T は任意の型パラメーター)、次のようになります。
- 安全: メソッドが配列の要素がのインスタンスであるという事実のみに依存している場合
T
- 安全でない: 配列がのインスタンスであるという事実に依存する場合
T[]
配列の実行時型に依存するものには、 type として返す、 typeT[]
のパラメーターに引数として渡す、をT[]
使用して配列型を取得する、配列.getClass()
の実行時型に依存するメソッドに渡す、などがList.toArray()
あります。Arrays.copyOf()
など
2) 上記の区別は複雑すぎて、自動的に簡単に区別することはできません。