Как использовать отражение Kotlin из Java

#java #kotlin #reflection #kotlin-reflect

#java #kotlin #отражение #kotlin-отражение

Вопрос:

Можно ли использовать отражение Kotlin из Java?

Я хочу получить KCallable из функции Kotlin в Java и использовать ее метод 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)
}
  

Возможно ли это вообще? Если да, то как получить экземпляр KCallable из кода Java?

Редактировать:

Я не могу использовать @JvmOverloads , как предложено, потому что количество аргументов, аргументов по умолчанию и их позиций могут быть произвольными.

Известная информация для вызова:

  • имена аргументов
  • их тип
  • их значение

РЕДАКТИРОВАТЬ 2:

Пример @JvmOverloads того, что здесь не работает:

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

Здесь вызов с одним String значением неоднозначен.

Ответ №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 used because method is compiled as static, so no instance needed to call it
        System.out.println(result); //Will print "A: default - B: test"
    }
}
  

Редактировать

Если вы ищете способ преодолеть ограничения удобного взаимодействия с Java, то вам действительно нужно получить экземпляр KCallable в вашем Java-коде.

Я считаю, что это невозможно без вспомогательной функции Kotlin:

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

Его использование в Java довольно простое:

 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"
    }
}
  

Комментарии:

1. Большое вам спасибо, но это не то, о чем я спрашивал. Мне действительно нужен метод отражения Kotlin callBy , потому @JvmOverloads что сбой в каком-то сценарии, например, во втором test методе с a параметром типа Int .

2. Вы имеете в fun test(a: Int = 11, c: String): String = "A: $a - C: $c" виду, что в том же файле есть другая функция? Ну, это действительно усложняет задачу, если вы хотите иметь возможность вызывать их оба из Java, используя значение по умолчанию для a . Это даже усложняет простой (неотражающий) вызов их из kotlin с помощью одного аргумента (причина, чтобы избежать двусмысленности, вам нужно будет указать его имя). Я считаю, что вам следует обновить вопрос с помощью этого дополнительного предложения. В качестве простого обходного пути я могу предложить просто переместить second test в другой файл.

3. Спасибо за ваше время и терпение, я обновлю свой вопрос. Проблема просто показана с дополнительной функцией точно так, как вы указали. Реальная проблема с with @JvmOverloads — это параметры позиционирования, которые могут использовать параметры по умолчанию до или после значений, отличных от значений по умолчанию, поэтому @JvmOverloads здесь не может помочь.

4. Большое вам спасибо, вспомогательная функция — это правильный путь! 🙂