如何从泛型代码调用扩展函数?能否以泛型方式调用run方法?
如何在Kotlin泛型代码中调用扩展函数?
咱们先拆解你遇到的问题:你的代码编译报错,核心原因是listOf(Foo(), Bar())被编译器推断成了List<Any>类型,map里的a是Any类型——而你定义的run()是Foo和Bar各自的扩展函数,Any并没有这个方法,编译器自然找不到对应的实现。
那要怎么用泛型方式调用run()呢?答案是给Foo和Bar加一个共同的约束接口,让泛型基于这个接口来工作,具体有两种常见方案:
方案1:接口+扩展函数(类似Swift的protocol扩展)
先定义一个接口作为Foo和Bar的共同父类型,然后给这个接口写扩展函数,在扩展函数里根据实际类型分发逻辑:
// 定义共同接口 interface RunnableEntity class Foo : RunnableEntity class Bar : RunnableEntity // 给接口写扩展函数,统一处理不同实现类的逻辑 fun RunnableEntity.run(): String { return when(this) { is Foo -> "Foo.run" is Bar -> "Bar.run" else -> throw IllegalArgumentException("不支持的类型") } } fun main(args: Array<String>) { // 明确指定列表类型为RunnableEntity,而不是默认的Any val x: List<RunnableEntity> = listOf(Foo(), Bar()) val y = x.map { it.run() } // 现在可以正常编译调用了 y.forEach { println(it) } // 输出 Foo.run 和 Bar.run }
方案2:接口默认方法(支持运行时多态)
如果希望子类可以自定义run()的实现(类似Swift里protocol默认实现被重写的场景),更推荐用Kotlin接口的默认方法,它支持运行时多态:
// 接口里定义带默认实现的方法 interface RunnableEntity { fun run(): String = "默认实现" } // Foo重写默认方法 class Foo : RunnableEntity { override fun run() = "Foo.run" } // Bar直接使用默认实现 class Bar : RunnableEntity fun main(args: Array<String>) { val x: List<RunnableEntity> = listOf(Foo(), Bar()) val y = x.map { it.run() } y.forEach { println(it) } // 输出 Foo.run 和 默认实现 }
关于模仿Swift的protocol和extension写法是否可行?
完全可行,而且这是Kotlin里非常推荐的代码组织方式,甚至Kotlin的设计本身就借鉴了这类模式:
- Swift的
protocol对应Kotlin的接口,用来定义契约; - Swift的
protocol extension对应Kotlin的接口扩展函数,可以给所有实现类批量添加工具方法,无需每个类重复实现; - 如果需要支持子类重写默认行为,Kotlin的接口默认方法比扩展函数更合适——因为扩展函数是静态解析的(编译时决定调用哪个实现),而接口默认方法是动态多态的(运行时根据实际类型调用)。
需要注意的是,扩展函数的静态解析特性:如果同时给接口和它的子类写了同名扩展函数,调用哪个取决于变量的编译时类型,而不是运行时类型。比如:
interface RunnableEntity class Foo : RunnableEntity fun RunnableEntity.run() = "接口扩展" fun Foo.run() = "Foo扩展" fun main() { val entity: RunnableEntity = Foo() println(entity.run()) // 输出「接口扩展」,因为编译时类型是RunnableEntity println((entity as Foo).run()) // 输出「Foo扩展」,编译时类型是Foo }
如果想要和Swift一样,让子类重写默认行为后调用子类的实现,接口默认方法是更贴合的选择。
内容的提问来源于stack exchange,提问作者robkuz
相关产品推荐
相关产品推荐

