Как перегрузить конструкторы в 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
}
Другие вопросы по тегам