You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中实现多个同签名函数式接口的解决方案探讨

实现多个同签名函数式接口的问题与解决方案

问题重现

当多个函数式接口的类型签名不同时,类可以正常实现这些接口:

typealias A = () -> Unit
typealias B = (something: String) -> Unit

class Test : A, B {
    override fun invoke() {
        TODO("Not yet implemented")
    }

    override fun invoke(something: String) {
        TODO("Not yet implemented")
    }
}

但如果添加签名相同的第三个函数式接口,编译器会抛出Conflicting Overloads或A supertype appears twice错误:

typealias A = () -> Unit
typealias B = (something: String) -> Unit
typealias C = (somethingElse: String) -> Unit

class Test : A, B, C {
    override fun invoke() {
        TODO("Not yet implemented")
    }

    override fun invoke(something: String) {
        TODO("Not yet implemented")
    }

    override fun invoke(somethingElse: String) {
        TODO("Not yet implemented")
    }
}

临时取巧方案(不推荐)

添加无意义的参数可以暂时绕过编译错误,但这属于投机取巧,后续新增同签名接口时仍会重复出现问题:

typealias C = (somethingElse: String, garbage: Unit?) -> Unit

值类尝试的局限性

尝试用值类区分参数类型时,由于值类会被JVM编译时消除,会出现Platform declaration clash: The following declarations have the same JVM signature (invoke(Ljava/lang/Object;)Ljava/lang/Object;)错误:

@JvmInline
value class BString(val value: String)

@JvmInline
value class CString(val value: String)

typealias A = () -> Unit
typealias B = (something: BString) -> Unit
typealias C = (somethingElse: CString) -> Unit

class Test : A, B, C {
    override fun invoke() {
        TODO("Not yet implemented")
    }

    override fun invoke(something: BString) {
        TODO("Not yet implemented")
    }

    override fun invoke(somethingElse: CString) {
        TODO("Not yet implemented")
    }
}

注:Kotlin KEEP 302(Binary Signature Name)提案预计会在未来解决此类JVM签名冲突问题,但当前需要其他解决方案。

正确解决方案:值类+显式定义接口

放弃使用类型别名,改为显式定义函数式接口,配合值类区分参数类型,即可正常实现多个同签名逻辑的接口:

@JvmInline
value class BString(val value: String)

@JvmInline
value class CString(val value: String)

interface A {
    operator fun invoke()
}

interface B {
    operator fun invoke(something: BString)
}

interface C {
    operator fun invoke(somethingElse: CString)
}

class Test : A, B, C {
    override operator fun invoke() {
        println("invoke A")
    }

    override operator fun invoke(something: BString) {
        println("invoke B - something = $something")
    }

    override operator fun invoke(somethingElse: CString) {
        println("invoke C - somethingElse = $somethingElse")
    }
}

fun main(args: Array<String>) {
    val handlerA = A::invoke
    val handlerB = B::invoke
    val handlerC = C::invoke

    val t = Test()

    handlerA(t) 
    handlerB(t, BString("hello B"))
    handlerC(t, CString("hello C"))
}

运行后输出:

invoke A
invoke B - something = BString(value=hello B)
invoke C - somethingElse = CString(value=hello C)

实际场景适配

比如需要让类同时实现Clickable和DoubleClickable(二者签名均为(Event) -> Unit),可以分别定义ClickEvent和DoubleClickEvent值类,再对应定义两个显式接口,即可实现无冲突的多接口实现。

内容的提问来源于stack exchange,提问作者Jan Vladimir Mostert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:15:29