Kotlin リフレクション

##自己紹介

iOS(iOSの方が長い)とAndroidアプリのエンジニア。
ここ半年はAndroidメイン。
Oisixで働いています。

---

##概要
Javaなど、各言語で備えるリフレクション機能のKotlin版。
クラス構造(プロパティなど)を読み取ったり書き換えたりする機能。KClass(クラス参照)、KProperty(プロパティ参照)を利用する。

https://kotlinlang.org/docs/reference/reflection.html



---

##Projectの設定

kotlin標準ライブラリ(kotlin-stdlib-xxx)には含まれていない(fileサイズ削減のため)のでapp/build.gradleに下記を追加。

```gradle

dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}

```

※上記が無くてもリフレクションでビルドエラーにはならないが、値が返ってこなかったりエラーログが出力されたりした。

---

##file size

kotlin-reflectのサイズは4.8KB。

![スクリーンショット 2017-05-24 7.38.24.png](https://qiita-image-store.s3.amazonaws.com/0/37373/37da5489-b3a7-2fa9-4666-3737a2fe4612.png "スクリーンショット 2017-05-24 7.38.24.png")

---

##Class References

KotlinのClass参照はKClass型。

```kotlin
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が取れる

```kotlin
val j:Class<User> = User::class
```

---

##KotlinのClassリファレンスからプロパティ取得

```kotlin:
val kClass = User::class
kClass.memberProperties.forEach { p ->
   Log.d(TAG, p.name + ":" + p.get(user))
}
```

---

Userクラスにprivateなpropertyがあると、IllegalCallableAccessExceptionがthrowされる

```kotlin:
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と同じ）

```kotlin:
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リファレンスからもプロパティ取得できる

```kotlin:
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)

```kotlin:

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クラスになっている。

```kotlin:
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の派生インターフェースは下記３種類が存在する。
また、各インターフェースはMutable版(KMutableProperty1)が存在している

* KProperty0<R>：javaクラスのstatic変数を取得するときに利用
* KProperty1<T, out R>：先ほどの通り。メンバーのプロパティ取得時に利用
* KProperty2<D, E, out R>：拡張プロパティ取得時に利用

---

##まとめ

* プロパティ周りはJavaと同じような感じでアクセスできそう。
* デバッグ時のクラス内容出力などに利用できそう
* Function Referencesなど、他にもリフレクションは機能はいろいろありそう
* apkサイズ大きくするほどかは考えた方が良いかも

---

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