Kotlin扩展功能访问Java私有字段

问题描述:

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

假设我有一个JavaABCABC只有一个私人字段mPrivateField。我想在Kotlin中写一个扩展函数,无论出于什么原因都使用该字段。

public class ABC { 
    private int mPrivateField; 

} 

科特林功能是:

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

我得到的错误是:

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

任何方式让周围这种限制?

+0

你为什么要需要访问java ** private **字段? –

+0

它来自一个外部编译库,我希望在不包括整个源代码的情况下扩展到我的项目中。该库具有Android日历事件的“获取”方法,我希望将“插入日历事件”功能添加到类中。 –

+1

修改外部库**私人**字段是*危险*。它可能会粉碎你的应用程序。您可以使用java [反射](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html)更改字段值。 –

首先吸气,你需要以获得Field并启用它可以在科特林访问,例如:

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 
     [email protected] value; 
    } 
} 
+0

这是一个干净的解决方案,它以**内联**方式解决问题。无需定义单独的扩展吸气剂。谢谢 –

+0

@ kosiara-BartoszKosarzycki当然,如果你喜欢,你可以将函数变成**内联函数。 Howerver,当你想要抽象操作时,扩展是必须的,例如:'testExtFunc(action:(Int) - > R)',将读取逻辑与'action'逻辑分开,并且可以重新使用读取函数'testExtFunc'任何地方。那么你可以使用它作为'testExtFunc(foo)'或'testExtFunc(bar)'它们就像在''''''''''''''''应用'和'.etc在kotlin中一样。 –

这是不可能的设计。扩展函数基本上解析为接收器作为其第一个参数的静态函数。因此,扩展功能

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

编译成类似:

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

现在很清楚的看到你不能访问的$receiver私有成员,因为他们是,那么,私人。

如果你真的想要访问该成员,你可以使用反射,但你会失去所有保证。

+0

嗯,我希望kotlin家伙自动化反射方法一代来简化未来的私人领域获得者...虽然我同意在大多数情况下它不是安全的 –

+3

那么这不会发生。没有人应该鼓励这样的反思。 – nhaarman

+6

Android平台上的轶事提交:[另一天,访问另一个私人字段](https://android.googlesource.com/platform/libcore/+/81abb6fb7332dfe62ff596ffb250d8aec61895df%5E!/)。在这种情况下,谷歌不得不回滚***私人***成员的重命名,因为Facebook通过反思访问它。 – nhaarman

就像nhaarman建议我用反射来访问有问题的字段。具体来说,我创建了用于反射内部中提到的类(即ABC

可悲的是访问在科特林扩展功能的私人领域,是不可能的,因为的2017年七月

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 
} 
+1

你可以直接使用'Class#getDeclaredField'和'Field#getInt'。因为你确切知道你想要获得什么领域。 –