可変長引数の全体像
可変長引数(varargs)は、メソッドに「0個以上の引数」を渡せる仕組みです。シグネチャでは Type... name と書き、呼び出し側は m()、m(a)、m(a, b, c) のように好きな個数で呼べます。実体は「配列」なので、メソッド内部では name は Type[] として扱えます。重要なのは「最後の引数にしか置けない」「オーバーロードの曖昧さ」「配列扱いゆえの副作用(浅いコピーではない)」の3点です。
基本の使い方とシグネチャ
宣言と内部の見え方
static int sum(int... xs) { // 可変長引数
int total = 0;
for (int x : xs) total += x; // 実体は int[] 配列
return total;
}
sum(); // 0(空配列)
sum(1); // 1
sum(1, 2, 3); // 6
JavaType...は「最後の引数」にしか書けません。int... xs, String nameのような並びは不可です。- メソッド内部では
xs.lengthやxs[0]のように配列としてアクセスします。
明示的に配列を渡す
呼び出し側で配列を渡すと、そのまま varargs に収まります。既存配列を使い回したいときに便利です。
int[] a = {1, 2, 3};
sum(a); // 6(配列そのもの)
Java便利な場面と実用例
文字列の連結や整形
static String join(String sep, String... parts) {
return String.join(sep, parts);
}
System.out.println(join(", ", "A", "B", "C")); // A, B, C
Javaログやメッセージのフォーマット(printf系)
System.out.printf(String format, Object... args) は典型例です。フォーマット文字列に対して、引数を好きな個数だけ渡します。
System.out.printf("id=%s, score=%d%n", "X-1", 99);
System.out.printf("no args%n"); // 引数なしでもOK
Java小さなユーティリティ
static <T> boolean anyNull(T... xs) {
for (T x : xs) if (x == null) return true;
return false;
}
anyNull("A", null, "B"); // true
Java重要ポイントの深掘り:設計とオーバーロード
末尾にしか置けない、1つだけ
可変長は「最後の引数で1つだけ」。複数の varargs は定義できません。
// NG: 複数 varargs
// void f(int... a, String... b);
Javaオーバーロードの曖昧さに注意
固定長と varargs を混在させると、解決が曖昧になりがちです。意図が明確に読み取れるように設計しましょう。
void print(String s) { /* ... */ }
void print(String... ss) { /* ... */ }
// print("A"); はどちら? → 固定長が優先されるが、読み手に負担。避けるべき。
Java指針:
- 固定長のオーバーロードと varargs を併置するなら、役割を分ける(メソッド名を変える)。
- 最低引数数があるなら、別引数で受けてから「追加分」を varargs にする。
static String label(String head, String... tail) { /* head は必須、tail は0個以上 */ }
Javaパフォーマンスとアロケーション
呼び出しごとに配列が1つ作られます。高頻度・小さな呼び出しが大量にあるときは固定長オーバーロードやコレクション入力の方が効く場合があります。
// 高頻度なら
void add3(int a, int b, int c) { /* ... */ }
// または
void addAll(IntStream xs) { /* ... */ }
Javanull と配列の扱い(誤解しやすい点)
引数としての null と、要素としての null
- 呼び出し側で「配列そのものを null」にすると NPE の原因になります(varargsは配列に展開される前提)。
- 要素としての null は通常許容されます(ただしメソッド仕様次第)。
// NG: varargs に null の配列参照を渡す
int[] ref = null;
// sum(ref); // 実装によっては NullPointerException
// OK: 要素に null(参照型の場合)
static int countNonNull(String... xs) {
int n = 0;
for (String x : xs) if (x != null) n++;
return n;
}
Javaプリミティブ vs ラッパー
int... と Integer... は別物です。オートボクシングが発生するとコストが上がるため、数値中心ならプリミティブ側を選ぶのが無難です。
ジェネリクスと @SafeVarargs
非具象化型(reifiable でない)とヒープ汚染の注意
T... は実体が T[] ですが、型消去により実行時には Object[] 相当です。内部で配列に異なる型を混ぜるとヒープ汚染の危険があります。
static <T> void collect(java.util.List<T> out, T... xs) {
for (T x : xs) out.add(x);
}
Java安全に使う指針:
- 受け取った varargs 配列を外へ公開しない(そのまま返さない)。
- 配列に他型を挿入しない。
- メソッドが「安全である」ことが明らかな場合、final/static なメソッドに
@SafeVarargsを付けて警告を抑止します。
@SafeVarargs
static <T> java.util.List<T> listOf(T... xs) {
return java.util.Arrays.asList(xs); // 配列は公開しない(リストのビューに留める)
}
Java可変長引数の前処理と転送(forwarding)
受け取って別メソッドへ渡す
varargs を受けて、別の varargs へ「そのまま渡す」には配列のまま渡します。
static void debug(String tag, Object... args) {
System.out.printf("[%s] ", tag);
System.out.printf(java.util.Locale.JAPAN, "%s%n", java.util.Arrays.toString(args));
}
static void info(Object... args) {
debug("INFO", args); // そのまま配列で転送
}
Java先頭に必須引数、後ろは自由
必須の文脈(タグ、ヘッダなど)と、可変の本体を分けると設計が明快になります。
static void log(String level, Object... xs) { /* ... */ }
log("WARN", "id=", 123, "bad"); // level は必須、xs は0個以上
Java例題で身につける
例 1: 文字列の結合(区切りあり・なし)
static String concat(String... parts) {
return String.join("", parts);
}
static String concatWith(String sep, String... parts) {
return String.join(sep, parts);
}
System.out.println(concat("A", "B", "C")); // ABC
System.out.println(concatWith(", ", "A", "B", "C")); // A, B, C
Java例 2: スコアの平均(空は Optional.empty)
import java.util.OptionalDouble;
static OptionalDouble average(int... xs) {
if (xs.length == 0) return OptionalDouble.empty();
int sum = 0;
for (int x : xs) sum += x;
return OptionalDouble.of((double) sum / xs.length);
}
average(); // empty
average(10, 20, 30); // 20.0
Java例 3: Comparator を作る(ジェネリクス+varargs)
@SafeVarargs
static <T> java.util.Comparator<T> chain(java.util.Comparator<T>... cs) {
return (a, b) -> {
for (var c : cs) {
int r = c.compare(a, b);
if (r != 0) return r;
}
return 0;
};
}
var byLen = java.util.Comparator.comparingInt(String::length);
var byLex = java.util.Comparator.naturalOrder();
var cmp = chain(byLen, byLex); // 長さ→辞書順
Java例 4: 必須+可変のログ
static void logf(String level, String format, Object... args) {
System.out.printf("[%s] " + format + "%n", level, args);
}
logf("INFO", "id=%s, ok=%b", "X-1", true);
Java仕上げのアドバイス(重要部分のまとめ)
可変長引数は「最後に1つだけ」「内部では配列」「呼び出し時に配列が1つ生成される」を軸に使う。オーバーロードは曖昧さを避け、必須引数+varargsの形にすると明快。nullは「配列参照のnull」を渡さない、要素のnullは仕様で扱う。数値はプリミティブでコストを抑え、ジェネリクスでは配列を公開せず安全に扱い、必要なら @SafeVarargs を付ける。転送は配列で渡し、printf系など既存APIの設計も参考にする——この型が身につけば、柔軟で読みやすいメソッド設計が自然に書けます。
