1)ジェネリックと可変引数の特定の問題について、インターネットとStackOverflowに多くの例があります。基本的には、型パラメーター型の可変数の引数がある場合です。
<T> void foo(T... args);
Javaでは、可変引数は、コンパイル時に単純な「再書き込み」を受ける構文上の砂糖です。型の可変引数パラメーターは、型のパラメーターにX...
変換されますX[]
。そして、このvarargsメソッドが呼び出されるたびに、コンパイラーはvarargsパラメーターに入るすべての「変数引数」を収集し、のように配列を作成しますnew X[] { ...(arguments go here)... }
。
これは、varargs型がのような具象である場合にうまく機能しますString...
。のような型変数の場合T...
、T
その呼び出しの具象型であることがわかっている場合にも機能します。たとえば、上記のメソッドがクラスの一部で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として返す、type T[]
のパラメータに引数として渡す、をT[]
使用して配列タイプを取得する、配列.getClass()
のランタイムタイプに依存するメソッドに渡すなどがList.toArray()
あります。Arrays.copyOf()
、など
2)上記で説明した区別は複雑すぎて、自動的に簡単に区別できません。