Вот минимальный демонстрационный код, который показывает эту проблему:
interface A
fun <T1, T2> test() where T2 : T1, T2 : A {}
Когда я пытаюсь скомпилировать его, компилятор будет жаловаться:
Ошибка: (81, 25) Kotlin: параметр Type не может иметь никаких других границ, если он ограничен другим параметром типа
Я прочитал Специфика языка Kotlin, но вы найдете только следующее ограничение:
Параметр type не может указывать себя как свою собственную привязку, и несколько параметров типа не могут указывать друг друга как ограничение циклическим образом.
Он не объясняет, какое ограничение я встречаю.
Я изучаю трекер Kotlin, и я нахожу проблему об этом ограничении: Разрешить наследование параметра типа из другого параметра типа и класса: KT-13768. Однако этот вопрос был отклонен по следующей причине (обновление от 6 мая 2017 года: этот вопрос был вновь открыт Станиславом Ерохиным):
Я не думаю, что мы сможем правильно скомпилировать код JVM, если удалим это ограничение.
Андрей Бреслав
Итак, возникает вопрос: почему мы не можем правильно скомпилировать код JVM, если мы удалим это ограничение?
Эта же демонстрация работает в Scala:
trait A
def test[T1, T2 <: T1 with A](): Unit = {}
Это означает, что Scala может скомпилировать код для JVM. Почему Котлин не может? Является ли это ограничением для обеспечения разрешимого подтипирования в Kotlin (я думаю, что подтипирование невозможно для Scala (Scala имеет систему Turing-complete type). Kotlin может хотеть разрешимый подтипирование, как С#.)?
Обновление после ответа от @erokhins (qaru.site/info/387165/...):
Есть некоторые тонкие проблемы при поддержке чего-то, запрещенного Java, но разрешенного JVM, особенно в Java-совместимости. Я нахожу интересную проблему при перекодировании в байт-код, созданный с помощью скаляса. Я изменяю код Scala в демо, как показано ниже:
trait A
trait B
def test[T1 <: B, T2 <: T1 with A](t1: T1, t2: T2): Unit = {}
class AB extends A with B
Scalac сгенерирует следующую подпись:
// signature <T1::LB;T2:TT1;:LA;>(TT1;TT2;)V
// descriptor: (LB;LB;)V
public <T1 extends B, T2 extends T1 & A> void test(T1, T2);
Вызов test
с test(new AB, new AB)
в Scala будет успешным, так как Scalas вызывает подпись (LB;LB;)V
; но вызывать с помощью test(new AB(), new AB());
в Java не удастся, так как Java вызывает подпись (LB;Ljava/lang/Object;)V
, вызывая java.lang.NoSuchMethodError
во время выполнения. Это означает, что генерируется скаляр, что-то не может быть вызвано на Java после ослабления этого ограничения. Котлин может встретить ту же проблему после расслабления.