Kotlinの可変長引数とフォーマットを利用した置き換えで詰まったのでメモ
可変長引数
メソッドの引数として定義するときは vararg
を付けます。
fun someMethod(vararg args: String) { args.forEach { println(it) } } someMethod("one") // ok someMethod("one, two") // ok someMethod(arrayOf("one", "two")) // error someMethod(arrayListOf("one", "two")) // error
フォーマット
文字列内で置き換えできるやつです。
fun greet(name: String): { val greetText = "Hello %s." println(greetText.format(name)) } greet("kotlin") // Hello kotlin.
ハマったとこ
可変長引数の展開
次のようなフォーマットに対して
val singleGreet = "Hello %s."
次の2つは結果が異なります。
- フォーマットに直接指定
singleGreet.format("everyone") // Hello everyone.
- フォーマットに関数を経由して指定
fun greet(vararg args: String) { singleGreet.format(args) } greet("everyone") // Hello [Ljava.lang.String;@30dae81.
これは、greet関数の引数で可変長引数としているため、formatにはArray
として利用されるからです。
避けるためには、可変長引数を展開してあげる必要があります。
展開するには、可変長引数の先頭に *
を付けます。(spread演算子)
- 可変長引数を展開
fun greet(vararg args: String) { singleGreet.format(*args) } greet("everyone") // Hello everyone.
どちらもコンパイルが通るのでアレですが、空気を読んでほしかった部分はあります。
冒頭で書いた関数も、spread演算子を使えばコンパイルが通ります。
someMethod(*arrayOf("one", "two")) // ok
フォーマットが複数ある場合も展開するときれいに収まります。
val multiGreet = "Hello %1\$s, and %2\$s." // これでも出来るが `*` を使うほうが変更に強い fun greetMultiNoSpread(vararg args: String) { multiGreet.format(args[0], args[1]) } greetMultiNoSpread("mother", "father") // Hello mother, and father. fun greetMulti(vararg args: String) { multiGreet.format(*args) } greetMulti("mother", "father") // Hello mother, and father.
raw stringsでの展開
"
3つで囲むやつです。改行などもそのまま出力できます。
フォーマットが複数ある場合は $
を使う必要があるのですが、raw stringsだとバックスラッシュでのエスケープができません。
val rawEscapedMultiGreet = """ |Hello %1\$s. |Hello %2\$s. """.trimMargin() // Unresolved reference: s // コンパイルエラー
次のように書きます。
val rawMultiGreet = """ |Hello %1${'$'}s. |Hello %2${'$'}s. """.trimMargin()
${}
のString Templatesを利用して$
に置き換えます。
参考
KotlinでString.formatを使う / Qiita
Variable number of arguments (Varargs) / Kotlin Programming Language