4

我有两个构造函数,它们的不同之处仅在于它们的 lambda 返回类型。有没有办法让它们超载?我试图使用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)}))
4

2 回答 2

6

您不能定义具有仅在泛型参数上不同的签名的构造函数(在您的情况下,它是 的泛型参数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)
于 2016-09-21T12:00:37.757 回答
3

以 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
}
于 2016-09-21T12:14:58.207 回答