Qiita で Bundle を少しだけ簡単に書くという記事を書いた。今回はそれの Kotlin バージョンとなる。

ソースコード

/**
 * Bundle オブジェクトを作成する utility method.
 *
 * @param args key と value の pair
 */
fun bundle(vararg args: Pair<String, Any?>): Bundle = Bundle().put(*args)

/**
 * すでにある Bundle オブジェクトに key と value を複数追加する utility method.
 *
 * @param args key と value の pair
 */
fun Bundle.put(vararg args: Pair<String, Any?>): Bundle {
    return args.fold(this, { bundle, pair ->
        val (key, value) = pair
        when (value) {
            null -> bundle.putString(key, null)
            is Boolean -> bundle.putBoolean(key, value)
            is BooleanArray -> bundle.putBooleanArray(key, value)
            is Bundle -> bundle.putBundle(key, value)
            is Byte -> bundle.putByte(key, value)
            is ByteArray -> bundle.putByteArray(key, value)
            is String -> bundle.putString(key, value)
            is Char -> bundle.putChar(key, value)
            is CharArray -> bundle.putCharArray(key, value)
            is CharSequence -> bundle.putCharSequence(key, value)
            is Double -> bundle.putDouble(key, value)
            is DoubleArray -> bundle.putDoubleArray(key, value)
            is Float -> bundle.putFloat(key, value)
            is FloatArray -> bundle.putFloatArray(key, value)
            is Short -> bundle.putShort(key, value)
            is ShortArray -> bundle.putShortArray(key, value)
            is Int -> bundle.putInt(key, value)
            is IntArray -> bundle.putIntArray(key, value)
            is Long -> bundle.putLong(key, value)
            is LongArray -> bundle.putLongArray(key, value)
            is Parcelable -> bundle.putParcelable(key, value)
            is Serializable -> bundle.putSerializable(key, value)
            is Array<*> -> {
                if (value.size > 0 && value.all { it is String }) {
                    bundle.putStringArray(key, value as Array<String>)
                } else if (value.size > 0 && value.all {it is CharSequence}) {
                    bundle.putCharSequenceArray(key, value as Array<CharSequence>)
                } else if (value.size > 0 && value.all {it is Parcelable }) {
                    bundle.putParcelableArray(key, value as Array<Parcelable>)
                } else {
                    throw IllegalArgumentException(key + "に対応する値が解釈できない.")
                }
            }
            else -> throw IllegalArgumentException(key + "に対応する値が解釈できない.")
        }
        bundle
    })
}

使用例

以下の様な感じで使える:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Bundle オブジェクト生成 {hoge: "fuga", hage: [1, 2, 3]}
    val b = bundle("hoge" to "fuga", "hage" to arrayOf(1, 2, 3))

    // Bundle にあたかも put メソッドがあるかのように {hehe: "ouie"} 追加
    b.put("hehe" to "ouie")
    println(b)  // Bundle[{hage=[Ljava.lang.Integer;@a0250eb, hehe=fufu, hoge=fuga, hoho=ouie}]
}

拡張関数

Kotlin だと何が有利かというところで 1 つめ。 拡張関数という仕組みがあり、既存クラスにあたかもそういうメソッドを持っているかのように関数を追加できる。 上記例の fun Bundle.put(vararg args: Pair<String, Any?>): Bundle がそれである。 この仕組のお陰で別途ユーティリティクラスを作成してそれを呼ぶようなことをしなくても直接オブジェクトから呼べて自然な実装となる。

第一級関数

Java だと必ずクラスを作ってその中にメソッドを書かなければならないが Kotlin は一番外側に関数を記述可能。 上記例の fun bundle(vararg args: Pair<String, Any?>): Bundle がそれである。 しかもこの外側の関数をそのまま import できる (Java だとちょっと違うが static インポートに当たるか) のでソースコードがスッキリする。

to は custom operator

"hoge" to "fuga"to って何だろう。言語仕様だろうか。などと思ったかもしれないが、これは custom operator でビルトインのライブラリに以下のように定義されている:

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

この infix というのを付けると自由な演算子が追加できるらしい。 この例だと任意のクラス A, B に対し to 演算子を適用すると Pair<A, B> インスタンスを生成して返す、というところか。

以前 Kotlin にはタプルがあったらしいが、これがあるので廃止されたらしい。

Hello World が短い

関係ないが Kotlin の Hello World が短い。以下で良い:

fun main(args: Array<String>) = println("Hello Kotlin!")

Java は以下:

public class Java {
    public static void main(String...args) {
        System.out.println("Hello World");
    }
}