Kotlin extension function access Java private field

22,678

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:

  1. Create extension
fun<T: Any> T.accessField(fieldName: String): Any? {
    return javaClass.getDeclaredField(fieldName).let { field ->
        field.isAccessible = true
        return@let field.get(this)
    }
}

  1. 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

Share:
22,678
kosiara - Bartosz Kosarzycki
Author by

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, 2020

Comments

  • kosiara - Bartosz Kosarzycki
    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 field mPrivateField. 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
    kosiara - Bartosz Kosarzycki almost 7 years
    Well 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
    holi-java almost 7 years
    you can using Class#getDeclaredField & Field#getInt directly. since you know exactly what field you want to get.
  • kosiara - Bartosz Kosarzycki
    kosiara - Bartosz Kosarzycki almost 7 years
    That's a clean solution which resolves the issue in an inline manner. No need to define a separate extension getter. Thanks
  • holi-java
    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 the action logic, and you can reuse the read function testExtFunc anywhere. then you can uses it as, testExtFunc(foo) or testExtFunc(bar) they are just like as let/run/apply and .etc in kotlin.
  • nhaarman
    nhaarman almost 7 years
    Well that's not gonna happen. No one should encourage reflection like that.
  • nhaarman
    nhaarman almost 7 years
    Anecdotal 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
    Morteza Rastgoo over 3 years
    How to get a field with type of listener, like mOnCheckedChangeListener in CompoundButton?
  • Adrenal1ne
    Adrenal1ne over 3 years
    Super ! Works for me. Thx