Get Kotlin class from string a call a method in it

1.6k views Asked by At

I have 2 simple classes in kotlin

package com.sample.repo
class SampleClassA() {
    fun test(): String {
        return "Do things A way"
    }
}

package com.sample.repo
class SampleClassB() {
    fun test(): String {
        return "Do things B way"
    }
}

Now i have a configuration file that tells me which class to use. Let's say i have a string val className = "SampleClassA" // assuming all classes are in same package

  • I want obtain this class and invoke the test function in it I was able to do below
fun `some random test`() {
val className = "SampleClassA"
val packageName = "com.sample.repo"
val kClass = Class.forName("$packageName.$className").kotlin
val method = kClass.members.find { it.name == "test" }
// How do i call this method ??
    }
}
2

There are 2 answers

0
Nikita Chegodaev On

You should create an object of the class and then call method on it. Example:

//...code from your example
val method = kClass.members.find { it.name == "test" }!!
val obj = kClass.primaryConstructor?.call()
val result = method.call(obj)
println(result)
2
gidds On

I wouldn't do it that way. Instead, I'd require that the classes you're choosing between implement some common interface, which you can then refer to directly. For example:

interface Testable {
    fun test(): String
}

 

package com.sample.repo
class SampleClassA() : Testable {
    override fun test() = "Do things A way"
}

 

package com.sample.repo
class SampleClassB() : Testable {
    override fun test() = "Do things B way"
}

 

fun `some random test`() {
    val className = "SampleClassA"
    val packageName = "com.sample.repo"
    val testable = Class.forName("$packageName.$className").kotlin
                        .createInstance() as Testable
    testable.test()
}

I don't know if this applies to OP, but judging from some of the questions asked here on StackOverflow, many people are coming to Kotlin from weakly-typed languages where it's common to use ‘string typing’ to fudge the lines between types, to assume that developers can always be trusted, and that it's fine to discover problems only at runtime. Of course, it's only natural to try to apply the patterns and techniques you're familiar with when learning a new language.

But while that style of programming is possible in Kotlin (using reflection), it's rarely a good fit. If you'll excuse one of my standard rants, reflection is slow, ugly, fragile, insecure, and hard to maintain; it's easy to get wrong, and forces you to handle most errors at runtime. Don't get me wrong: reflection is a very valuable tool, and there are situations where it's vital, such as writing frameworks, plug-ins, some forms of dependency injection, build tools, and similar. But reflection should be a tool of last resort — for general application coding, there's almost always a better approach, usually one that's more concise, easier to read, performs better, spots more problems at compile-time, can be autocompleted in your IDE, and works with the language and its type system, not against it.

Kotlin is a strongly-typed language; it has a fairly sophisticated type system (and type inference, so you don't need to keep repeating yourself), which is safer and smarter, turns many errors into compile-time errors, allows many optimisations, and is effectively self-documenting (making more explicit the contract between called code and its callers). It's better to try to work with the type system when you can, rather than subvert if (which is what reflection does).

The example above uses reflection to create an instance of a class which is assumed to implement the Testable interface (and will give ugly errors at runtime if the class isn't available, doesn't implement that interface, or doesn't have a public constructor with no required params), but after that uses normal, typed code which is much safer.

(In fact, depending how your test code is structured, you might find a way to configure it with Testable instances rather than String classnames, and avoid reflection altogether. That would be simpler and safer still.)