Menu

Java の可変長引数(Varargs): 任意の数の引数を受け取るメソッド

Java の可変長引数(...)でメソッドが任意の数の引数を受け取れる仕組み、それが配列になる理由、可変長引数は最後に1つだけというルール、そして空呼び出しとあいまいさの落とし穴。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

1つのメソッドで任意の数の引数

メソッドにいくつ引数が必要か、あらかじめ分からないことがあります。あるときは max(3, 9) を、次の瞬間には max(1, 7, 4, 2, 8) を使いたくなるかもしれません。可変長引数がなければ、引数の数ごとにオーバーロードを書くか、呼び出し側に配列を作らせるしかありません。可変長引数(variable arguments の略)を使うと、1つのメソッドで同じ型の値を 0 個、1 個、または多数受け取れます。

パラメータの型のうしろにドット3つ(...)を付けて宣言します:

呼び出し側はばらばらの値を渡し、メソッド内では numbers は本物の配列になります。これがすべてのポイントです。コンパイラが引数をまとめて配列にしてくれるのです。

可変長引数は単なる配列

実行時に魔法はありません。int... numbers と宣言されたパラメータは、メソッドに入った時点でまさに int[] です。length を調べ、添字でアクセスし、他の配列と同じようにループで回せます。

本当に配列なので、すでに持っている配列を可変長引数のメソッドに渡すこともできます。どちらの形式も受け取れます:

ルール: 最後に、そして1つだけ

2つのルールが可変長引数をあいまいでなく保ち、コンパイラはその両方を強制します:

  1. 可変長引数のパラメータは最後でなければならない。 通常のパラメータが先に来て、そのあと ... パラメータが残りをすべて吸収します。
  2. メソッドが持てる可変長引数は最大で1つ。 2つあると、どこで一方が終わり、もう一方が始まるのかを見分けることが不可能になります。

これはコンパイルできます。しかし可変長引数を先に置くと(static void log(Object... values, String level))コンパイルエラーになります: varargs parameter must be the last。必須パラメータ level は常に ... より前に来ます。

すでに可変長引数を使っている場所

実はずっと可変長引数のメソッドを呼んできました。System.out.printfString.format が典型例で、その第2パラメータは Object... args です:

List.of(...)Arrays.asList(...)、そして多くのビルダー風 API も可変長引数です。だからこそ、自分で配列に包まなくても任意の数の要素を渡せるのです。

可変長引数とオーバーロード

可変長引数とオーバーロードは、1つ意外な形で影響し合います。通常のオーバーロードと可変長引数のオーバーロードがどちらも呼び出しに一致しうる場合、Java は より具体的な ほう、つまり引数の数が固定のメソッドを優先します:

pick(1, 2) は両方に一致しますが、Java はより近い適合である可変長引数でないオーバーロードを選びます。可変長引数のメソッドは、引数の数が固定のオーバーロードがどれも一致しないときにだけ実行されます。たいていはこれが望む動作ですが、可変長引数のオーバーロードを追加すると、呼び出しがどのメソッドに解決されるかが知らないうちに変わってしまうことがある、ということでもあります。

空呼び出しと null 呼び出しに注意

2つの落とし穴が人をつまずかせます。1つ目: 可変長引数のメソッドを引数なしで呼ぶと、メソッドに渡されるのは null ではなく 空の配列 です。したがって numbers.length0 で、ループは何もしません。引数なしのケースに null チェックは不要です:

2つ目: リテラルの null を渡すのはあいまいで危険です。Java は count(null) を「null の要素が1つ」ではなく「可変長引数の配列全体が null」と解釈することがあり、その length を読んだときに NullPointerException を投げます。本当に null の要素を1つ渡したいなら、count((Object) null) のように明示してください。

次は: クラス

可変長引数は、メソッドパラメータでできることを総仕上げします。オーバーロードに加えて柔軟な引数リストが手に入ります。ここまでメソッドは static を付けてクラスの中にゆるく置かれていました。次は、クラスが本当はどう動くのかを見ていきます。データ(フィールド)とふるまい(メソッド)を自分の型にまとめること、それがオブジェクト指向 Java の核心です。

よくある質問

Java の可変長引数(varargs)とは何ですか?

可変長引数(varargs)を使うと、メソッドが同じ型の引数を任意の数だけ受け取れます。型のうしろに ... を付けて宣言します: int sum(int... numbers)。呼び出し側は 0 個、1 個、または多数の値を渡すことができ、メソッド内では numbers はふつうの配列です。これは構文糖衣で、Java がばらばらの引数を自動的に配列にまとめてくれます。

Java のメソッドは可変長引数を2つ以上持てますか?

いいえ。メソッドが持てる可変長引数は最大で1つだけで、しかもパラメータリストの最後でなければなりません。たとえば void log(String level, Object... values) です。もし最後でなかったら、Java は可変長の部分がどこで終わり、次のパラメータがどこから始まるのかを判断できません。通常のパラメータは常に ... パラメータより前に置きます。

Java で可変長引数と配列パラメータの違いは何ですか?

実行時にはほぼ同じです。可変長引数 int... は、メソッド内では int[] です。違いは呼び出し側にあります。可変長引数なら sum(1, 2, 3)(ばらばらの値)と書けますが、明示的な配列パラメータでは配列を作って渡さなければなりません: sum(new int[]{1, 2, 3})。可変長引数のメソッドは配列を直接渡しても受け取れるので、より柔軟な選択肢です。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める