元の記事 → Swift to Kotlinチートシート
Dependencyの設定
Kotlinでリフレクションを使うには、Dependencyを追加する必要がある。
dependencies {
   implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
クラスオブジェクトからインスタンスの作成
val foo = createInstance(Foo::class)
fun <T: Any> createInstance(type: KClass<T>): T {
    return type.java.newInstance()
}
TはAnyの制約をつけないとオプショナルも含まれてしまうため、KClass<T>が成立せずコンパイルエラーになる。
メンバー変数・定数出力
fun <T: Any> print(type: KClass<T>, obj: T) {
    type.memberProperties.forEach {
        println("${it.name}: ${it.get(obj)}")
    }
}
文字列からenumを取得
ちょっとトリッキーで、ワーニングを抑制する必要がある。
    fun <T: Enum<*>>getEnumValue(type: KClass<T>, name: String?) : T? {
        try {
            @Suppress("UPPER_BOUND_VIOLATED", "UNCHECKED_CAST")
            val result = java.lang.Enum.valueOf<Enum<*>>(type.java as Class<Enum<*>>, name)
            return result as? T
        } catch (e: IllegalArgumentException) {
            return null
        }
    }
Androidで memberProperties が異様に遅い
Androidで memberProperties を参照すると異様に時間がかかる現象が見られている。
class PlainClass {}
fun test() {
    print(PlainClass::class)
    print(View::class)
    print(ConstraintLayout::class)
}
fun print(kClass: KClass<*>) {
    val before = System.currentTimeMillis()
    kClass.memberProperties
    val after = System.currentTimeMillis()
    Log.d("TEST", "${kClass.simpleName} ${after - before} ms")
}
Xperia Z3 (Android5)で上記を試した結果。
D/TEST: PlainClass 1226 ms
D/TEST: View 6154 ms
D/TEST: ConstraintLayout 9443 ms
他の端末でも試したが、速度の違いはあれやはり遅かった。
Kotlinを使わずJavaでfieldを参照しても遅いようなので、この問題はどうしようもないかもしれない。
値のセット、アノテーション
@IBOutlet がついたプロパティーにViewからスネークケースにしたidの子Viewを探し出してセットする例。
※ただし、上記のmemberProperties遅い問題があるので実用レベルではない。同じようなことを実用レベルでやろうとすると、ButterKnifeみたいにAPTを使う必要があるかも
@Target(AnnotationTarget.PROPERTY)
@MustBeDocumented
annotation class IBOutlet
open class ViewController() {
    lateinit var rootView: View
    fun bindIBOutlets() {
        this::class.memberProperties.forEach {
            checkProperty(it)
        }
    }
    private fun checkProperty(property: KProperty1<out ViewController, Any?>) {
        if (property.findAnnotation<IBOutlet>() == null) {
            return
        }
        if (property !is KMutableProperty1) {
            Log.w("IBOutlet", "IBOutlet property [${property.name}] is immutable !!")
            return
        }
        try {
            val idString = property.name.toSnakeCase()
            val activity = MainActivity.instance
            val viewId = activity.resources.getIdentifier(idString, "id", activity.packageName)
            val view = rootView.findViewById<View>(viewId)
            if (view != null) {
                property.setter.call(this, view)
            } else {
                Log.w("IBOutlet", "Can't find view id [${idString}] !!")
            }
        } catch (e: IllegalCallableAccessException) {
            Log.w("IBOutlet", "Can't access IBOutlet property [${property.name}] !!")
        }
    }
}
fun String.toSnakeCase(): String {
    var canGoNextBlock = false
    return map {
        val separator = if (canGoNextBlock && it.isUpperCase()) "_" else ""
        canGoNextBlock = it.isLowerCase()
        separator + it.toLowerCase()
    }.joinToString("")
}