Exercise: Function caller
Your task is to implement methods of a class that is used to call function references with constant values specified by type. This class should have the following methods:
setConstant - sets a constant value for a given type.
call - calls a function reference with constant values specified by type.
If a constant value for a given type is not specified, an exception should be thrown. Unless the parameter with this type is optional, then its default argument should be used.
class FunctionCaller {
inline fun <reified T> setConstant(value: T) {
setConstant(typeOf<T>(), value)
}
fun setConstant(type: KType, value: Any?) {
TODO()
}
fun <T> call(function: KFunction<T>): T {
TODO()
}
}
Example usage:
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
}
This problem can either be solved in the below playground or you can clone
kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in advanced/reflection/FunctionCaller.kt. You can find there starting code, example usage and unit tests.
Hint: To support optional parameters, you should use callBy instead of call.
Once you are done with the exercise, you can check your solution
here.
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 {
inline fun <reified T> setConstant(value: T) {
setConstant(typeOf<T>(), value)
}
fun setConstant(type: KType, value: Any?) {
TODO()
}
fun <T> call(function: KFunction<T>): T {
TODO()
}
}
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)
}
}