web-dev-qa-db-ja.com

Kotlin拡張機能アクセスJavaプライベートフィールド

Kotlinを使用するときにJavaのプライベートフィールドにアクセスしたい拡張機能

Java class ABCがあるとします。 ABCには、プライベートフィールドmPrivateFieldが1つだけあります。何らかの理由でそのフィールドを使用する拡張機能をKotlinで作成したいと思います。

public class ABC {
    private int mPrivateField;

}

Kotlin関数は次のようになります。

private fun ABC.testExtFunc() {
    val canIAccess = this.mPrivateField;
}

私が得ているエラーは次のとおりです:

Cannot access 'mPrivateField': It is private in 'ABC'

回避その制限の任意の方法?

まず、 フィールド を取得し、Kotlinでaccessibleにできるようにする必要があります。次に例を示します。

val field = ABC::class.Java.getDeclaredField("mPrivateField")

field.isAccessible = true

次に、宣言クラスのインスタンスから Field#getInt を使用して、フィールド値をIntとして読み取ることができます。次に例を示します。

val it: ABC = TODO()

val value = field.getInt(it)

最後に、拡張メソッドは次のようになります。

private inline fun ABC.testExtFunc():Int {
    return javaClass.getDeclaredField("mPrivateField").let {
        it.isAccessible = true
        val value = it.getInt(this)
        //todo
        return@let value;
    }
}
22
holi-java

これは設計上不可能です。拡張関数は、基本的にレシーバーを最初のパラメーターとする静的関数に解決されます。したがって、拡張機能

fun String.foo() {
  println(this)
}

次のようなものにコンパイルします。

public static void foo(String $receiver) {
  System.out.println($receiver);
}

$receiverのプライベートメンバーにアクセスできないことは明らかです。

本当にそのメンバーにアクセスしたい場合、リフレクションを使用してアクセスできますが、すべての保証が失われます。

8
nhaarman

nhaarmanが示唆したように、問題のフィールドにアクセスするためにreflectionを使用しました。具体的には、言及したクラスで内部的にリフレクションを使用するゲッターを作成しました(つまり、ABC

2017年7月の時点で、Kotlin拡張機能のプライベートフィールドに悲しいことにアクセスできません

fun ABC.testExtFunc() {
    val canIAccess = this.getmPrivateField()
}

fun ABC.getmPrivateField() : Int {
    val field = this.javaClass.declaredFields
            .toList().filter { it.name == "mPrivateField" }.first()
    field.isAccessible = true
    val value = field.get(this)
    return value as Int
}

holi-Javaの答えを汎用型で拡張する:

  1. 拡張機能を作成
fun<T: Any> T.accessField(fieldName: String): Any? {
    return javaClass.getDeclaredField(fieldName).let { field ->
        field.isAccessible = true
        return@let field.get(this)
    }
}

  1. プライベートフィールドにアクセス
val field = <your_object_instance_with_private_field>
                .accessField("<field_name>")
                    as <object_type_of_field_name>

例:

class MyClass {

    private lateinit var mObject: MyObject

}

val privateField = MyClass()
                .accessField("mObject")
                    as MyObject

1