Kotlinの vararg(可変長引数)は、表面的にはシンプルですが、配列との関係・スプレッド演算子(*)・オーバーロード解決・パフォーマンス特性など、理解が浅いと設計ミスにつながりやすい機能です。
ここでは「単なる使い方」ではなく、なぜそう動くのか/どこで使うべきかまで含めて解説します。
目次
varargとは何か(基本)
vararg は「引数の個数を可変にできる仕組み」です。
fun printAll(vararg messages: String) {
for (msg in messages) {
println(msg)
}
}
呼び出し側
printAll("Hello")
printAll("Hello", "World")
printAll("Kotlin", "is", "fun")
重要な前提
varargで受け取った値は 関数内ではまとめて扱われる- 見た目は「複数引数」だが、内部的には配列
varargの正体:実体は「配列」
Kotlinでは、vararg パラメータは 配列として扱われます。
fun sample(vararg nums: Int) {
println(nums.size)
}
この nums は、関数内では IntArray です。
型ごとの内部表現
| 宣言 | 関数内での型 |
|---|---|
vararg x: Int | IntArray |
vararg x: Double | DoubleArray |
vararg x: Boolean | BooleanArray |
vararg x: String | Array<String> |
この点は パフォーマンスやJava連携 に直結します。
varargは1つだけ & 位置制約
varargは1つしか定義できない
// コンパイルエラー
fun bad(vararg a: Int, vararg b: String) {}
varargが最後でない場合
vararg は原則として 最後の引数 に置きます。
もし最後でない場合、後続引数は名前付き引数で指定する必要があります。
fun example(vararg nums: Int, label: String) {}
example(1, 2, 3, label = "test") // OK
スプレッド演算子(*)は必須知識
配列を vararg に渡す場合
fun sum(vararg nums: Int): Int = nums.sum()
val array = intArrayOf(1, 2, 3)
// ❌ エラー
// sum(array)
// ✅ 正しい
sum(*array)
* の意味
array→ 「配列1つを渡す」*array→ 「配列を展開して複数引数として渡す」
補足(重要)
* による展開は、状況によっては配列コピーが発生します。
sum(0, *array, 99)
このような場合、新しい配列を生成して結合する必要があるため、高頻度処理ではコストになる可能性があります。
vararg + 通常引数(よくある実務パターン)
fun log(level: String, vararg messages: String) {
println("[$level]")
messages.forEach { println(it) }
}
log("INFO", "start", "processing", "end")
実務での代表例
- ログ出力
- SQLのIN句構築
- UIイベントの引数束ね
- DSL(後述)
ジェネリクスとvararg
fun <T> asList(vararg items: T): List<T> =
items.toList()
val a = asList(1, 2, 3)
val b = asList("A", "B")
混在型の場合
val mixed = asList(1, "A", true)
この場合、共通の上位型が推論され、典型的には List<Any?> になります。
Javaとの相互運用
Kotlin → Java
fun printAll(vararg values: String) {}
Java側
printAll("a", "b", "c");
Java → Kotlin
void log(String... messages)
Kotlin側
log("a", "b")
log(*arrayOf("a", "b"))
Javaの ... と Kotlin の vararg は 概念的に同等ですが、
Kotlin側では * が必要になる点が違いです。
varargとオーバーロードの注意点(誤解されやすい)
fun test(a: Int) {}
fun test(vararg a: Int) {}
これは コンパイルエラーではありません
test(1)→ 固定長引数の方が優先されるtest(1, 2)→ vararg が呼ばれる
ただし
- APIとしては 読み手に意図が伝わりにくい
- デフォルト引数などと組み合わさると、可読性が急激に落ちる
実務では避ける設計が無難
パフォーマンスの観点(重要)
vararg は呼び出し時に 配列を生成します。
repeat(1_000_000) {
foo(1, 2, 3)
}
- 毎回配列が生成される
- spread(
*)を使うとコピーも発生し得る
対策
- 高頻度処理では
List/Arrayを直接受け取る - APIの「使いやすさ」と「性能」を意識して選択
DSL・API設計での使いどころ
良い例(DSL)
fun html(vararg children: Tag) {}
html(
h1("Title"),
p("Hello"),
p("World")
)
悪い例(乱用)
fun process(vararg params: Any?)
- 型安全性が崩壊
- 呼び出し側も実装側も意図が不明確
よくある勘違いまとめ
| 勘違い | 実際 |
|---|---|
| varargはList | 実体は配列 |
| 配列はそのまま渡せる | * が必要 |
| 何個でも定義できる | 1つだけ |
| パフォーマンスに影響しない | 配列生成コストあり |
まとめ
varargは APIの使いやすさを高めるための機能- 内部では 配列が生成されることを理解する
*(スプレッド演算子)はコピーが発生し得る- 高頻度処理・大量データでは慎重に使う
- DSL・補助的API・ログ用途に特に向いている
以上、Kotlinのvarargについてでした。
最後までお読みいただき、ありがとうございました。










