Is it possible to use Kotlin reflection from Java?
I want to get KCallable
from Kotlin function in Java and use its method callBy
to call method with default arguments.
Example in Kotlin:
fun test(a: String = "default", b: String): String {
return "A: $a - B: $b";
}
fun main() {
val callable: KCallable<*> = ::test
val parameterB = callable.parameters[1]
val result = callable.callBy(mapOf(
parameterB to "test"
))
println(result)
}
Is it even possible? If so, how to get instance of KCallable
from Java code?
EDIT:
I cannot use @JvmOverloads
as suggested, because the number of arguments, default arguments and their positions can be arbitrary.
The known information for calling is:
EDIT 2:
Example of not working @JvmOverloads
here:
fun test(a: String = "default", b: String = "default"): String {
return "A: $a - B: $b";
}
Here calling with one String
value is ambiguous.
If file, where test
function was declared, is Utils.kt
, then it will be compiled into UtilsKt
class.
As documentation states:
Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the @JvmOverloads annotation.
So, after adding this annotation:
@JvmOverloads
fun test(a: String = "default", b: String): String {
return "A: $a - B: $b";
}
test
method may be called from java with a single parameter:
public class ReflectionInterop {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method test = UtilsKt.class.getDeclaredMethod("test", String.class);
String result = (String) test.invoke(null, "test"); //null used because method is compiled as static, so no instance needed to call it
System.out.println(result); //Will print "A: default - B: test"
}
}
EDIT
If you are looking for a way to overcome limitations of convenient java interop, then you indeed need to get an instance of KCallable
in your Java code.
I believe it is impossible without auxilary Kotlin function:
fun testReflection(): KCallable<*> = ::test
Its usage in Java is pretty simple:
public class ReflectionInterop {
public static void main(String[] args) {
KCallable<?> test = UtilsKt.testReflection(); //Assuming it is located in the same `Utils.kt` file
KParameter parameterB = test.getParameters().get(1);
String result = (String) test.callBy(new HashMap<>() {{
put(parameterB, "test");
}});
System.out.println(result); //Will print "A: default - B: test"
}
}