JavaのVarargs
1. 序章
VarargsはJava5 で導入され、1つのタイプの任意の数のパラメーターをサポートするメソッドの省略形を提供します。
この記事では、このコアJava機能の使用方法を説明します。
2. Varargsの前
Java 5より前は、任意の数の引数を渡したい場合は常に、配列内のすべての引数を渡すか、N個のメソッド(追加のパラメーターごとに1つ)を実装する必要がありました。
public String format() { ... }
public String format(String value) { ... }
public String format(String val1, String val2) { ... }
3. Varargsの使用
Varargs は、内部の配列を使用して、任意の数のパラメーターを自動的に処理できる新しい構文を導入することにより、ボイラープレートコードの記述を回避するのに役立ちます。
標準の型宣言とそれに続く省略記号を使用して、それらを定義できます。
public String formatWithVarArgs(String... values) {
// ...
}
そして今、次のように任意の数の引数を使用してメソッドを呼び出すことができます。
formatWithVarArgs();
formatWithVarArgs("a", "b", "c", "d");
前述のように、 varargsは配列であるため、通常の配列と同じように操作する必要があります。
4. ルール
Varargsは簡単に使用できます。 ただし、覚えておく必要のあるルールがいくつかあります。
- 各メソッドは、varargsパラメーターを1つだけ持つことができます
- varargs引数は最後のパラメーターである必要があります
5. ヒープ汚染
static String firstOfFirst(List<String>... strings) {
List<Integer> ints = Collections.singletonList(42);
Object[] objects = strings;
objects[0] = ints; // Heap pollution
return strings[0].get(0); // ClassCastException
}
テストでこの奇妙なメソッドを呼び出すと、次のようになります。
String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList());
assertEquals("one", one);
ここでは明示的な型キャストを使用していなくても、ClassCastExceptionが発生します。
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
5.1. 安全な使用法
varargsを使用するたびに、Javaコンパイラは指定されたパラメータを保持する配列を作成します。この場合、コンパイラは引数を保持する汎用型コンポーネントを含む配列を作成します。
ジェネリック型でvarargsを使用すると、致命的なランタイム例外のリスクが発生する可能性があるため、Javaコンパイラは安全でないvarargsの使用の可能性について警告します。
warning: [varargs] Possible heap pollution from parameterized vararg type T
varargs の使用は、次の場合にのみ安全です。
- 暗黙的に作成された配列には何も格納しません。 この例では、 リスト
その配列で - 生成された配列への参照がメソッドをエスケープしないようにします(これについては後で詳しく説明します)
メソッド自体がvarargsを安全に使用していることが確実な場合は、@SafeVarargsを使用して警告を抑制できます。
簡単に言えば、 varargs の使用法は、呼び出し元からメソッドに可変数の引数を転送するために使用する場合に安全です。
5.2. Varargsリファレンスのエスケープ
varargsの別の危険な使用法を考えてみましょう。
static <T> T[] toArray(T... arguments) {
return arguments;
}
最初は、toArrayメソッドは完全に無害に見えるかもしれません。 ただし、varargs配列を呼び出し元にエスケープさせるため、安全なvarargsの2番目のルールに違反します。
この方法がどのように危険であるかを確認するために、別の方法で使用してみましょう。
static <T> T[] returnAsIs(T a, T b) {
return toArray(a, b);
}
次に、このメソッドを呼び出すと、次のようになります。
String[] args = returnAsIs("One", "Two");
繰り返しになりますが、
- aおよびbをtoArrayメソッドに渡すには、Javaで配列を作成する必要があります
- Object [] は任意のタイプのアイテムを保持できるため、コンパイラーはアイテムを作成します
- toArray メソッドは、指定された Object[]を呼び出し元に返します
- 呼び出しサイトはString[]を予期しているため、コンパイラは Object[]を予期されたString[] にキャストしようとします。したがって、 ClassCastException
ヒープ汚染の詳細については、JoshuaBlochによるEffectiveJavaの項目32を読むことを強くお勧めします。
6. 結論
Varargs は、Javaで多くの定型文をなくすことができます。
また、配列との間の暗黙的な自動ボクシングのおかげで、はコードの将来性を保証する役割を果たします。
いつものように、この記事のすべてのコード例は、GitHubリポジトリで入手できます。