class FunctionCaller {
private var values: MutableMap<KType, Any?> =
mutableMapOf()
inline fun <reified T> setConstant(value: T) {
setConstant(typeOf<T>(), value)
}
fun setConstant(type: KType, value: Any?) {
values[type] = value
}
fun <T> call(function: KFunction<T>): T {
val args = function.parameters
.filter { param ->
values.containsKey(param.type)
}
.associateWith { param ->
val type = param.type
val value = values[type]
require(param.isOptional || value != null) {
"No value for $type"
}
value
}
return function.callBy(args)
}
}
Example solution in playground
import org.junit.After
import org.junit.Test
import kotlin.reflect.KFunction
import kotlin.reflect.KType
import kotlin.reflect.typeOf
import kotlin.test.assertEquals
class FunctionCaller {
private var values: MutableMap<KType, Any?> =
mutableMapOf()
inline fun <reified T> setConstant(value: T) {
setConstant(typeOf<T>(), value)
}
fun setConstant(type: KType, value: Any?) {
values[type] = value
}
fun <T> call(function: KFunction<T>): T {
val args = function.parameters
.filter { param ->
values.containsKey(param.type)
}
.associateWith { param ->
val type = param.type
val value = values[type]
require(param.isOptional || value != null) {
"No value for $type"
}
value
}
return function.callBy(args)
}
}
fun printStrIntNum(str: String, int: Int, num: Number) {
println("str: $str, int: $int, num: $num")
}
fun printWithOptionals(l: Long = 999, s: String) {
println("l: $l, s: $s")
}
fun main() {
val caller = FunctionCaller()
caller.setConstant("ABC")
caller.setConstant(123)
caller.setConstant(typeOf<Number>(), 3.14)
caller.call(::printStrIntNum)
// str: ABC, int: 123, num: 3.14
caller.call(::printWithOptionals)
// l: 999, s: ABC
}
class FunctionCallerTest {
var value: Any? = null
fun callStr(str: String) {
value = "callStr $str"
}
fun callInt(int: Int) {
value = "callInt $int"
}
fun callStringInt(str: String, int: Int) {
value = "callStringInt $str $int"
}
data class User(val id: Int, val name: String?, var surname: String?)
fun callUser(user: User) {
value = user
}
fun callWithDefault(c: Char, i: Int = 999, s: String = "XXX", l: Long) {
value = "callWithDefault $c $i $s $l"
}
@After
fun cleanUp() {
value = null
}
@Test
fun testWithString() {
val caller = FunctionCaller()
caller.setConstant("ABC")
caller.call(::callStr)
assertEquals("callStr ABC", value)
}
@Test
fun testWithInt() {
val caller = FunctionCaller()
caller.setConstant(123)
caller.call(::callInt)
assertEquals("callInt 123", value)
}
@Test
fun testWithStringInt() {
val caller = FunctionCaller()
caller.setConstant("DEF")
caller.setConstant(456)
caller.call(::callStringInt)
assertEquals("callStringInt DEF 456", value)
}
@Test
fun testWithUser() {
val caller = FunctionCaller()
val user = User(123, "DEF", "GHI")
caller.setConstant(user)
caller.call(::callUser)
assertEquals(user, value)
}
@Test
fun testIgnoredOptional() {
val caller = FunctionCaller()
caller.setConstant('Z')
caller.setConstant(123L)
caller.call(::callWithDefault)
assertEquals("callWithDefault Z 999 XXX 123", value)
}
}
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.