Java/Android/Kotlin: Reflection on private Field and call public methods on it
Asked Answered
I

4

27

Is it possiable to use reflection to access a object's private field and call a public methods on this field?

i.e

class Hello {
   private World word
}

class World {
   public BlaBlaBla foo()
}

Hello h = new Hello()

World world = reflect on the h


// And then 

world.foo()
Inspiration answered 8/1, 2018 at 22:25 Comment(0)
V
32

It’s possible to make private fields accessible using reflection. The following examples (both written in Kotlin) show it...

Using Java Reflection:

val hello = Hello()
val f = hello::class.java.getDeclaredField("world")
f.isAccessible = true
val w = f.get(hello) as World
println(w.foo())

Using Kotlin Reflection:

val hello = Hello()
val f = Hello::class.memberProperties.find { it.name == "world" }
f?.let {
    it.isAccessible = true
    val w = it.get(hello) as World
    println(w.foo())
}
Veer answered 8/1, 2018 at 22:41 Comment(3)
How could you access a private val which is not in a class using reflection?Bold
For properties you may need to do it.getter.call(hello) instead of it.get(hello)Amadis
Would this work for a private Kotlin extension function ??Spermous
A
36

To access private properties and functions of a class in Kotlin here are two useful extension functions:

inline fun <reified T> T.callPrivateFunc(name: String, vararg args: Any?): Any? =
    T::class
        .declaredMemberFunctions
        .firstOrNull { it.name == name }
        ?.apply { isAccessible = true }
        ?.call(this, *args)

inline fun <reified T : Any, R> T.getPrivateProperty(name: String): R? =
    T::class
        .memberProperties
        .firstOrNull { it.name == name }
        ?.apply { isAccessible = true }
        ?.get(this) as? R

Usage Example:

class SomeClass {

    private val world: World = World()

    private fun somePrivateFunction() {
        println("somePrivateFunction")
    }

    private fun somePrivateFunctionWithParams(text: String) {
        println("somePrivateFunctionWithParams()  text=$text")
    }
}

class World {
    fun foo(): String = "Test func"
}

// calling private functions:

val someClass = SomeClass()
someClass.callPrivateFunc("somePrivateFunction")
someClass.callPrivateFunc("somePrivateFunctionWithParams", "test arg")

// getting private member and calling public function on it:

val world = someClass.getPrivateProperty<SomeClass, World>("world")
println(world?.foo())

To use reflection in Kotlin add dependency:

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

Airship answered 28/12, 2019 at 8:32 Comment(0)
V
32

It’s possible to make private fields accessible using reflection. The following examples (both written in Kotlin) show it...

Using Java Reflection:

val hello = Hello()
val f = hello::class.java.getDeclaredField("world")
f.isAccessible = true
val w = f.get(hello) as World
println(w.foo())

Using Kotlin Reflection:

val hello = Hello()
val f = Hello::class.memberProperties.find { it.name == "world" }
f?.let {
    it.isAccessible = true
    val w = it.get(hello) as World
    println(w.foo())
}
Veer answered 8/1, 2018 at 22:41 Comment(3)
How could you access a private val which is not in a class using reflection?Bold
For properties you may need to do it.getter.call(hello) instead of it.get(hello)Amadis
Would this work for a private Kotlin extension function ??Spermous
G
2

If you like readable reusable easy to understand solution:

Define somewhere:

fun <T> Any.privateField(name: String): T {
    val field = this::class.java.getDeclaredField(name)
    field.isAccessible = true
    @Suppress("UNCHECKED_CAST")
    return field.get(this) as T
}

And use elsewhere:

val <T : TextInputLayout> T.startIconView: CheckableImageButton
    get() = privateField("startIconView")
Gaylordgaylussac answered 5/8, 2020 at 2:32 Comment(0)
M
1

Call private function with params using Java reflection:

inline fun <reified T> T.callPrivateFunc(name: String, vararg args: Any?): Any? {
    val classArray: Array<Class<*>> = args.map { it!!::class.java}.toTypedArray()
    return T::class.java.getDeclaredMethod(name, *classArray)
        .apply { isAccessible = true }
        .invoke(this, *args)
}
Mischievous answered 24/5, 2020 at 4:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.