【问题标题】:Kotlin extension function access Java private fieldKotlin 扩展函数访问 Java 私有字段
【发布时间】:2017-12-21 05:59:51
【问题描述】:

我想在使用 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'

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

【问题讨论】:

  • 为什么需要访问 java private 字段?
  • 它来自一个外部的、已编译的库,我想扩展它而不在我的项目中包含整个源代码。该库具有用于 Android 日历事件的“获取”方法,我想将“插入日历事件”功能添加到类中。
  • 修改外部库的private字段是危险的。它可能会破坏您的应用程序。您可以使用 java reflection 更改字段值。
  • 是的,我同意。但我只想得到那个字段。并像我扩展的类一样使用它的方法。这里没有真正的威胁......

标签: kotlin kotlin-extension kotlin-interop


【解决方案1】:

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

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;
    }
}

【讨论】:

  • 这是一个干净的解决方案,它以内联的方式解决了问题。无需定义单独的扩展 getter。谢谢
  • @kosiara-BartoszKosarzycki 当然,如果你愿意,你可以把函数做成一个inline函数。但是,当你想抽象一个操作时,扩展是必要的,例如:testExtFunc(action:(Int)->R),将读取逻辑与action逻辑分开,你可以在任何地方重用读取函数testExtFunc。那么您可以将其用作 testExtFunc(foo)testExtFunc(bar) 它们就像 let/run/apply 和 .etc 在 kotlin 中一样。
  • 如何获取一个监听器类型的字段,比如CompoundButton中的mOnCheckedChangeListener
【解决方案2】:

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

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

编译为:

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

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

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

【讨论】:

  • 好吧,我希望 kotlin 的家伙能够自动生成反射方法,以简化未来的私有字段获取器......虽然我同意在大多数情况下它是不安全的
  • 这不会发生。没有人应该鼓励这样的反思。
  • Android 平台中的轶事提交:Another day, another private field accessed。在这种情况下,Google 不得不回滚一个 private 成员的重命名,因为 Facebook 正在通过反射访问它。
【解决方案3】:

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

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

【讨论】:

  • 超级!为我工作。谢谢
【解决方案4】:

正如 nhaarman 建议的那样,我使用 reflection 来访问相关字段。具体来说,我创建了一个在提到的类(即ABC)内部使用反射的getter

遗憾的是,截至 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
}

【讨论】:

  • 你可以直接使用Class#getDeclaredField & Field#getInt。因为你确切地知道你想得到什么领域。
【解决方案5】:

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

【讨论】:

    猜你喜欢
    • 2015-04-02
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 2012-06-07
    • 2019-07-13
    相关资源
    最近更新 更多