#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 с помощью одного аргумента (причина, чтобы избежать двусмысленности, вам нужно будет указать его имя). Я считаю, что вам следует обновить вопрос с помощью этого дополнительного предложения. В качестве простого обходного пути я могу предложить просто переместить secondtest
в другой файл.3. Спасибо за ваше время и терпение, я обновлю свой вопрос. Проблема просто показана с дополнительной функцией точно так, как вы указали. Реальная проблема с with
@JvmOverloads
— это параметры позиционирования, которые могут использовать параметры по умолчанию до или после значений, отличных от значений по умолчанию, поэтому@JvmOverloads
здесь не может помочь.4. Большое вам спасибо, вспомогательная функция — это правильный путь! 🙂