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;
}
}
これは設計上不可能です。拡張関数は、基本的にレシーバーを最初のパラメーターとする静的関数に解決されます。したがって、拡張機能
fun String.foo() {
println(this)
}
次のようなものにコンパイルします。
public static void foo(String $receiver) {
System.out.println($receiver);
}
$receiver
のプライベートメンバーにアクセスできないことは明らかです。
本当にそのメンバーにアクセスしたい場合、リフレクションを使用してアクセスできますが、すべての保証が失われます。
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の答えを汎用型で拡張する:
fun<T: Any> T.accessField(fieldName: String): Any? {
return javaClass.getDeclaredField(fieldName).let { field ->
field.isAccessible = true
return@let field.get(this)
}
}
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