##自己紹介
iOS(iOSの方が長い)とAndroidアプリのエンジニア。
ここ半年はAndroidメイン。
Oisixで働いています。
##概要
Javaなど、各言語で備えるリフレクション機能のKotlin版。
クラス構造(プロパティなど)を読み取ったり書き換えたりする機能。KClass(クラス参照)、KProperty(プロパティ参照)を利用する。
##Projectの設定
kotlin標準ライブラリ(kotlin-stdlib-xxx)には含まれていない(fileサイズ削減のため)のでapp/build.gradleに下記を追加。
dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
※上記が無くてもリフレクションでビルドエラーにはならないが、値が返ってこなかったりエラーログが出力されたりした。
##file size
kotlin-reflectのサイズは4.8KB。
##Class References
KotlinのClass参照はKClass型。
data class User(val firstName: String, var lastName: String,private val secretName: String)
val user = User("Test", "Debug", "Hoge")
val c:KClass<User> = User::class
.javaすると、Java class referenceが取れる
val j:Class<User> = User::class
##KotlinのClassリファレンスからプロパティ取得
val kClass = User::class
kClass.memberProperties.forEach { p ->
Log.d(TAG, p.name + ":" + p.get(user))
}
Userクラスにprivateなpropertyがあると、IllegalCallableAccessExceptionがthrowされる
val kClass = User::class
kClass.memberProperties.forEach { p ->
try {
Log.d(TAG, p.name + ":" + p.get(user))
} catch (e: IllegalCallableAccessException) {
Log.d(TAG, p.name + " is private property.")
}
}
isAccessible = trueにするとprivateなpropertyも参照できる(javaと同じ)
val kClass = User::class
kClass.memberProperties.forEach { p ->
try {
p.isAccessible = true//これを設定すると、privateなpropertyも参照できる
Log.d(TAG, p.name + ":" + p.get(user))
} catch (e:Throwable) {
e.printStackTrace()
}
}
JavaのClassリファレンスからもプロパティ取得できる
val javaClass = User::class.java
javaClass.declaredFields.forEach { field ->
field.isAccessible = true
Log.d(TAG, field.name + ":" + field.get(user))
}
##Property References
クラス名::プロパティ名がProperty References。
KProperty型(Interface)
val firstName = User::firstName.get(user)
val javaGetter = User::firstName.javaGetter//ゲッターメソッド(Method型)。public final getFirstName()
val javaField = User::firstName.javaField//Field(Field型)。private final firstName
User::lastName.set(user, "lastNameDebug")//これで値できるセット
val sercretName = User::sercretName.get(user)//コンパイルエラー。private fieldはこの方法では参照できない
実際にプロパティを取得するときは、下記pがKProperty1<プロパティを持つクラス, プロパティの型>(KPropertyから派生)を実装したKProperty1Implクラス(内部で自動生成されるクラス)になっている。
Mutable(変更可能)なプロパティはKMutableProperty1Implクラスになっている。
val kClass = User::class
kClass.memberProperties.forEach { p:KProperty1<User, *> ->
try {
p.isAccessible = true//これを設定すると、privateなpropertyも参照できる
Log.d(TAG, p.name + ":" + p.get(user))
} catch (e:Throwable) {
e.printStackTrace()
}
}
KPropertyの派生インターフェースは下記3種類が存在する。
また、各インターフェースはMutable版(KMutableProperty1)が存在している
- KProperty0:javaクラスのstatic変数を取得するときに利用
- KProperty1:先ほどの通り。メンバーのプロパティ取得時に利用
- KProperty2:拡張プロパティ取得時に利用
##まとめ
- プロパティ周りはJavaと同じような感じでアクセスできそう。
- デバッグ時のクラス内容出力などに利用できそう
- Function Referencesなど、他にもリフレクションは機能はいろいろありそう
- apkサイズ大きくするほどかは考えた方が良いかも
#ご静聴ありがとうございました