如何从Java中使用Kotlin反射

huangapple go评论75阅读模式
英文:

How to use Kotlin reflection from Java

问题

在Java中是否可以使用Kotlin反射?

我想从Kotlin函数中在Java中获取KCallable,并使用其方法callBy调用带有默认参数的方法。

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);
}

这种做法是否可行?如果可以,如何在Java代码中获取KCallable的实例?

编辑:

我不能使用建议中的@JvmOverloads,因为参数的数量、默认参数及其位置都是任意的。

用于调用的已知信息包括:

  • 参数名称
  • 参数类型
  • 参数值

编辑2:

不起作用的@JvmOverloads示例:

fun test(a: String = "default", b: String = "default"): String {
    return "A: $a - B: $b";
}

在这里,使用一个String值进行调用是有歧义的。

英文:

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 = &quot;default&quot;, b: String): String {
    return &quot;A: $a - B: $b&quot;;
}

fun main() {
    val callable: KCallable&lt;*&gt; = ::test
    val parameterB = callable.parameters[1]

    val result = callable.callBy(mapOf(
        parameterB to &quot;test&quot;
    ))

    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:

  • names of arguments
  • their type
  • their value

EDIT 2:

Example of not working @JvmOverloads here:

fun test(a: String = &quot;default&quot;, b: String = &quot;default&quot;): String {
    return &quot;A: $a - B: $b&quot;;
}

Here calling with one String value is ambiguous.

答案1

得分: 1

如果声明了 test 函数的文件是 Utils.kt,那么它将被编译为 UtilsKt 类。

根据文档的说明:
>通常情况下,如果您编写了一个带有默认参数值的 Kotlin 函数,它在 Java 中只会以完整签名的形式可见,其中包含所有参数。如果您希望向 Java 调用者公开多个重载版本,可以使用 @JvmOverloads 注解。

因此,在添加了此注解之后:

@JvmOverloads 
fun test(a: String = "default", b: String): String {
    return "A: $a - B: $b";
}

test 方法可以在 Java 中以单个参数调用:

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 用于调用静态方法,因此不需要实例
        System.out.println(result); //将打印 "A: default - B: test";
    }
}

编辑

如果您正在寻找一种克服便捷的 Java 互操作性限制的方法,那么您确实需要在 Java 代码中获取 KCallable 的实例。

我认为在没有辅助的 Kotlin 函数的情况下是不可能的:

fun testReflection(): KCallable<*> = ::test

它在 Java 中的使用非常简单:

public class ReflectionInterop {
    public static void main(String[] args) {
        KCallable<?> test = UtilsKt.testReflection(); //假设它位于同一个 `Utils.kt` 文件中
        KParameter parameterB = test.getParameters().get(1);
        String result = (String) test.callBy(new HashMap<>(){{
            put(parameterB, "test");
        }});
        System.out.println(result); //将打印 "A: default - B: test";
    }
}
英文:

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 = &quot;default&quot;, b: String): String {
    return &quot;A: $a - B: $b&quot;;
}

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(&quot;test&quot;, String.class);
        String result = (String) test.invoke(null, &quot;test&quot;); //null used because method is compiled as static, so no instance needed to call it
        System.out.println(result); //Will print &quot;A: default - B: test&quot;
    }
}

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&lt;*&gt; = ::test

Its usage in Java is pretty simple:

public class ReflectionInterop {
    public static void main(String[] args) {
        KCallable&lt;?&gt; 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&lt;&gt;() {{
            put(parameterB, &quot;test&quot;);
        }});
        System.out.println(result); //Will print &quot;A: default - B: test&quot;
    }
}

huangapple
  • 本文由 发表于 2020年10月26日 23:20:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/64539969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定