Перегрузка по методу Котлина
Следующая декларация является законной в Котлине.
fun foo(): String = "foo_1"
fun <T> foo(): T = "foo_2" as T
В качестве байт-кода мы получаем:
public final static foo()Ljava/lang/String;
// signature <T:Ljava/lang/Object;>()TT;
// declaration: T foo<T>()
public final static foo()Ljava/lang/Object;
Также возможно вызвать оба эти метода из Kotlin.
Проблема возникает, когда я пытаюсь вызвать любой из них с Java:
ClassKt.foo()
Двусмысленный звонок. Оба метода совпадают...
Как избежать такой проблемы? Как бороться с такими методами? Что если у сторонней библиотеки kt такая же проблема?
Приведенный выше пример является синтетическим.
3 ответа
Почему он работает с Kotlin для начала... В Java есть два метода, такие как:
private static String test() {
return "";
}
private static <T> T test() {
return null;
}
приведет к ошибке времени компиляции. И для разработчиков Java это вроде очевидно, эти методы будут иметь стирание одного и того же типа. Но это правило, навязанное javac
не по JVM
где этот код работает. Так javac
не обрабатывает два метода как имеющие другой тип возвращаемого значения как перегрузки. Что ж, kotlin
это другой язык, и так как он работает на JVM
(который ожидает корректный байт-код), он позволяет обрабатывать методы только с типом возвращаемого значения, отличным от перегрузок. Мне еще предстоит взглянуть на байт-код и понять, как это происходит; также кажется, что это будет работать только для общего кода, поэтому стирание типов может немного отличаться в случае kotlin.
Теперь должно быть очевидно, почему не удается вызвать такой метод из Java. Котлин предлагает для этого аккуратное решение: @JvmName("someDistinctName")
, Я не совсем уверен, как это работает под капотом тоже... пока, хотя я предполагаю, что это создаст метод моста.
РЕДАКТИРОВАТЬ
@JvmName
переименует метод на уровне байт-кода.
Вы можете использовать @JvmName, чтобы отличать код при вызове от java:
@JvmName("fooString")
fun foo(): String = "foo_1"
fun <T> foo(): T = "foo_2" as T
Это позволит вызывать метод String в Java с ClassKt.fooString()
, который разрешает конфликт.
Простым решением было бы написать вспомогательный метод в Kotlin и просто вызвать его.
Другим способом использования только Java было бы получение MethodHandle
для обоих методов и их использования:
MethodHandle MH_fooString = lookup().findStatic(ClassKt.class, "foo", methodType(String.class));
MethodHandle MH_fooT = lookup().findStatic(ClassKt.class, "foo", methodType(Object.class));
String foo = (String) MH_fooString.invokeExact();
Это не так просто и требует обработки исключений.