LoginSignup
18
24

More than 5 years have passed since last update.

Kotlin リフレクション

Last updated at Posted at 2017-05-24
1 / 16

自己紹介

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。

スクリーンショット 2017-05-24 7.38.24.png


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サイズ大きくするほどかは考えた方が良いかも

ご静聴ありがとうございました

18
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
24