35

我想在使用 Kotlin扩展功能时访问 Java 的私有字段

假设我有一个JavaABCABC只有一个私有字段mPrivateField。我想在 Kotlin 中编写一个扩展函数,它出于任何原因使用该字段。

public class ABC {
    private int mPrivateField;

}

Kotlin函数将是:

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

我得到的错误是:

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

有什么办法可以绕过这个限制?

4

5 回答 5

56

首先,您需要获取一个Field并使其可以在 Kotlin 中访问,例如:

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

field.isAccessible = true

Int然后,您可以通过Field#getInt从声明类的实例中读取字段值,例如:

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;
    }
}
于 2017-07-16T18:18:55.090 回答
11

这在设计上是不可能的。扩展函数本质上解析为以接收者为第一个参数的静态函数。因此,扩展功能

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

编译为:

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

现在很明显,您无法访问 的私有成员$receiver,因为它们是私有的。

如果您真的想访问该成员,您可以使用反射来实现,但您将失去所有保证。

于 2017-07-16T18:08:17.210 回答
6

使用以下扩展函数获取私有变量

fun <T : Any> T.getPrivateProperty(variableName: String): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        return@let field.get(this)
    }
}

设置私有变量值 获取变量

fun <T : Any> T.setAndReturnPrivateProperty(variableName: String, data: Any): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        field.set(this, data)
        return@let field.get(this)
    }
}

获取变量使用:

val bool = <your_class_object>.getPrivateProperty("your_variable") as String

设置和获取变量使用:

val bool = <your_class_object>.setAndReturnPrivateProperty("your_variable", true) as Boolean
val str = <your_class_object>.setAndReturnPrivateProperty("your_variable", "Hello") as String
于 2020-08-10T09:47:10.133 回答
3

正如nhaarman建议的那样,我使用反射来访问相关字段。具体来说,我创建了一个在提到的类(即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
}
于 2017-07-16T18:15:34.207 回答
1

用泛型类型扩展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

于 2019-07-20T12:50:09.433 回答