こんにちは、かつコーチです。
「引数の数が決まっていないメソッドを作りたいけれど、オーバーロードで全パターン用意するのは大変」と感じたことはありませんか。
Javaには可変長引数(varargs)という、引数の数を可変にできる仕組みがあります。
この記事では、可変長引数の書き方と、内部で配列として扱われる仕組みを解説します。
可変長引数とは
引数の数を可変にできる仕組み
可変長引数とは、メソッドの引数として渡す値の個数を、呼び出し側が自由に決められるようにする仕組みです。
型の後ろに...(ドット3つ)を付けて宣言します。
public int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
このメソッドは、sum(1, 2)のように2個渡しても、sum(1, 2, 3, 4, 5)のように5個渡しても、引数なしのsum()でも呼び出せます。
なぜ可変長引数が必要なのか
System.out.printf()やString.format()のように、渡す値の個数が状況によって変わるメソッドは実務でよく登場します。
これをオーバーロードだけで実現しようとすると、「引数1個用」「2個用」「3個用」と、必要になるたびにメソッドを増やさなければなりません。
可変長引数を使えば、1つのメソッド定義で「0個から好きな数まで」の引数に対応できます。
前回の記事で触れたとおり、「引数のパターンが数種類に決まっている」場合はオーバーロード、「個数が不定」な場合は可変長引数、という使い分けが基本方針です。
基本の書き方・実装手順
手順1:可変長引数の基本構文
public class Calculator {
public int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.sum()); // 0
System.out.println(calc.sum(1, 2)); // 3
System.out.println(calc.sum(1, 2, 3, 4)); // 10
}
}
可変長引数のメソッド内部では、numbersはint[]型の配列として扱われます。
そのため、拡張for文(for (int n : numbers))でそのまま繰り返し処理ができます。
手順2:配列を直接渡すこともできる
可変長引数には、値を1つずつ渡す以外に、配列をそのまま渡すこともできます。
int[] scores = {80, 90, 70};
System.out.println(calc.sum(scores)); // 240
内部的には配列として扱われるため、この2通りの呼び出し方はどちらも同じ結果になります。
つまずきやすい設定・注意点:他の引数との併用ルール
可変長引数は、メソッドの引数リストの最後にしか置けません。
// OK:可変長引数が最後にある
public void method1(String name, int... scores) { }
// NG:可変長引数の後に別の引数がある
public void method2(int... scores, String name) { } // コンパイルエラー
また、1つのメソッドに可変長引数は1つまでしか定義できません。
よくあるつまずきポイント・エラー対処
オーバーロードとの組み合わせであいまいになる
固定引数のメソッドと可変長引数のメソッドを同時に定義すると、意図と違うメソッドが呼ばれることがあります。
❌Before
public class Notifier {
public void send(String message) {
System.out.println("固定引数版: " + message);
}
public void send(String... messages) {
System.out.println("可変長引数版: " + messages.length + "件");
}
public static void main(String[] args) {
Notifier notifier = new Notifier();
notifier.send("お知らせ"); // どちらが呼ばれる?
}
}
実際に実行すると「固定引数版: お知らせ」と表示されます。
私は最初、可変長引数版が呼ばれると思い込んでいて、想定と違う出力に戸惑いました。
Javaは「より具体的に一致するメソッド」を優先して選ぶため、引数の数がぴったり一致する固定引数版が優先されます。
✅After
意図が伝わりにくくなるため、同名メソッドで固定引数版と可変長引数版を両方定義するのは避け、役割ごとにメソッド名を分けるのが安全です。
public class Notifier {
public void sendSingle(String message) {
System.out.println("単一送信: " + message);
}
public void sendMultiple(String... messages) {
System.out.println("複数送信: " + messages.length + "件");
}
}
nullを渡したときの挙動
可変長引数にnullを直接渡すと、コンパイラが「配列としてのnull」と解釈し、実行時にNullPointerExceptionが発生することがあります。
calc.sum((int[]) null); // 実行時にNullPointerExceptionの可能性
意図せずnullが渡る可能性がある場合は、メソッド内でnumbers == nullのチェックを入れておくと安全です。
応用・一歩先の使い方
可変長引数と拡張for文・Streamの相性
可変長引数の中身は配列なので、Java 8以降のStreamAPIとも組み合わせられます。
public int sumWithStream(int... numbers) {
return java.util.Arrays.stream(numbers).sum();
}
Arrays.stream()で配列をStreamに変換すれば、合計・最大値・平均値などの集計処理を簡潔に書けます。
Stream APIの詳しい使い方は、関数型プログラミングを扱う別記事で改めて解説する予定です。
まとめ
この記事のポイント
- 可変長引数(
型... 変数名)を使うと、引数の個数を呼び出し側が自由に決められる - メソッド内部では配列として扱われ、拡張for文でそのまま処理できる
- 可変長引数は引数リストの最後に1つだけ置ける
- 固定引数版と可変長引数版を同名で併用すると、意図と違うメソッドが呼ばれることがあるため注意する
次に読むべき記事
- 引数のパターンが数種類に決まっている場合の書き方は、メソッドのオーバーロードの記事を参照してください
- パッケージとimportの仕組みについては、次の記事で解説します
タグ: Java, 中級者向け, 基本文法