Как перегрузить конструкторы в kotlin отличается от типа возврата лямбда
У меня есть два конструктора, которые отличаются только по типу возвращаемого лямбда-выражения. Есть ли вариант как их перегрузить? Я пытался использовать аннотацию JvmOverloads, но это не сработало.
constructor(db : Database, handler: ( transaction: Transaction) -> Unit) : this(db, Handler<Transaction>( {handler.invoke(it)}))
@JvmOverloads
constructor(db : Database, handler: ( transaction: Transaction) -> Any) : this(db, Handler<Transaction>( {handler.invoke(it)}))
2 ответа
Вы не можете определить конструкторы с сигнатурами, которые отличаются только общими параметрами (в вашем случае это общие параметры для Function1<in P1, out R>
) потому что подписи будут конфликтовать после стирания дженериков.
Тем не менее, в вашем случае Unit
это подтип Any
, и с тех пор Function<in P1, out R>
ковариантен по R
, вы можете передать функцию, которая возвращает Unit
ко второму конструктору, так что просто удалите первый.
Упрощенный пример:
class C(val action: (Int) -> Any)
fun main(args: Array<String>) {
val f: (Int) -> Unit = { println(it) }
C(f)
}
В более сложных случаях рассмотрите возможность перехода к фабричным функциям: в отличие от конструкторов, их можно комментировать @JvmName
чтобы избежать столкновения подписей:
@JvmName("createCUnit")
fun createC(f: (Int) -> Unit) = C(f)
fun createC(f: (Int) -> Any) = C(f)
При нацеливании на бэкэнд JVM все классы Kotlin компилируются в байт-код JVM. Проблема с байт-кодом Java является стирание типа. Это означает, что вся информация о дженериках удаляется (это проблема Java, а не Kotlin).
Объявление функционального типа (transaction: Transaction) -> Unit
Эквивалентно использованию этого типа: Function1<Transaction, Unit>
, Однако для байт-кода JVM оба Function1<Transaction, Unit>
а также Function1<Transaction, Any>
подобные.
Это означает, что оба ваших конструктора имеют одинаковую подпись в мире JVM.
Вы можете "симулировать" конструкторы, используя companion object
class MyClass {
constructor(db: Database, h: Handler<Transaction>)
companion object {
operator fun invoke(db: Database, handler: (transaction: Transaction) -> Unit) = MyClass(db, Handler<Transaction>({ handler.invoke(it) }))
@JvmName("alternative_constructor")
operator fun invoke(db: Database, handler: (transaction: Transaction) -> Any) = MyClass(db, Handler<Transaction>({ handler.invoke(it) }))
}
}
И использование выглядит так:
fun main(args: Array<String>) {
val db = Database()
MyClass(db, Handler { }) //real constructor
MyClass(db){ } //version from companion object
}