Kotlin extension function access Java private field
Solution 1
First, you need to obtain a Field and enable it can be accessible in Kotlin, for example:
val field = ABC::class.java.getDeclaredField("mPrivateField")
field.isAccessible = true
Then, you can read the field value as Int
by Field#getInt from the instance of the declaring class, for example:
val it: ABC = TODO()
val value = field.getInt(it)
Last, your extension method is looks like as below:
private inline fun ABC.testExtFunc():Int {
return javaClass.getDeclaredField("mPrivateField").let {
it.isAccessible = true
val value = it.getInt(this)
//todo
return@let value;
}
}
Solution 2
That is not possible by design. Extension functions essentially resolve to static functions with the receiver as its first parameter. Thus, an extension function
fun String.foo() {
println(this)
}
compiles to something like:
public static void foo(String $receiver) {
System.out.println($receiver);
}
Now it's clear to see you cannot access private member of $receiver
, since they're, well, private.
If you really want to access that member, you could do so using reflection, but you'll lose all guarantees.
Solution 3
Get private variable using below extension functions
fun <T : Any> T.getPrivateProperty(variableName: String): Any? {
return javaClass.getDeclaredField(variableName).let { field ->
field.isAccessible = true
return@let field.get(this)
}
}
Set private variable value get the variable
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)
}
}
Get variable use:
val bool = <your_class_object>.getPrivateProperty("your_variable") as String
Set and get variable use:
val bool = <your_class_object>.setAndReturnPrivateProperty("your_variable", true) as Boolean
val str = <your_class_object>.setAndReturnPrivateProperty("your_variable", "Hello") as String
Solution 4
Just as nhaarman suggested I used reflection to access the field in question. Specifically I created a getter which used reflection internally on the class mentioned (that is ABC
)
Sadly accessing private fields in Kotlin extension function is not possible as of July 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
}
Solution 5
Extending holi-java's answer with a generic type:
- Create extension
fun<T: Any> T.accessField(fieldName: String): Any? {
return javaClass.getDeclaredField(fieldName).let { field ->
field.isAccessible = true
return@let field.get(this)
}
}
- Access private field
val field = <your_object_instance_with_private_field>
.accessField("<field_name>")
as <object_type_of_field_name>
Example:
class MyClass {
private lateinit var mObject: MyObject
}
val privateField = MyClass()
.accessField("mObject")
as MyObject
kosiara - Bartosz Kosarzycki
I'm a senior Java/Android developer constantly developing his skills more here: https://sites.google.com/site/bkosarzyckiaboutme/
Updated on August 10, 2020Comments
-
kosiara - Bartosz Kosarzycki over 3 years
I'd like to access Java's private field when using Kotlin extension function.
Suppose I have a Java class
ABC
.ABC
has only one private fieldmPrivateField
. I'd like to write an extension function in Kotlin which uses that field for whatever reason.public class ABC { private int mPrivateField; }
the Kotlin function would be:
private fun ABC.testExtFunc() { val canIAccess = this.mPrivateField; }
the error I'm getting is:
Cannot access 'mPrivateField': It is private in 'ABC'
Any way of getting around that limitation?
-
kosiara - Bartosz Kosarzycki almost 7 yearsWell I hope kotlin guys automate the reflection method generation to simplify private field getters in the future... although I agree that in most cases it's not safe
-
holi-java almost 7 yearsyou can using
Class#getDeclaredField
&Field#getInt
directly. since you know exactly what field you want to get. -
kosiara - Bartosz Kosarzycki almost 7 yearsThat's a clean solution which resolves the issue in an inline manner. No need to define a separate extension getter. Thanks
-
holi-java almost 7 years@kosiara-BartoszKosarzycki Of course, you can if you like, you can make the function to an inline function. Howerver, the extension is necessary when you want to make an operation abstract, for example:
testExtFunc(action:(Int)->R)
, separate the read logic from theaction
logic, and you can reuse the read functiontestExtFunc
anywhere. then you can uses it as,testExtFunc(foo)
ortestExtFunc(bar)
they are just like aslet
/run
/apply
and .etc in kotlin. -
nhaarman almost 7 yearsWell that's not gonna happen. No one should encourage reflection like that.
-
nhaarman almost 7 yearsAnecdotal commit in the Android platform: Another day, another private field accessed. In this case Google had to rollback the rename of a private member because Facebook was accessing it through reflection.
-
Morteza Rastgoo over 3 yearsHow to get a field with type of listener, like
mOnCheckedChangeListener
inCompoundButton
? -
Adrenal1ne over 3 yearsSuper ! Works for me. Thx