Kotlin跨类调用协程扩展函数失败原因咨询
核心问题:扩展函数的作用域限制
你定义的myProducerFunction是在MyProducerClass类内部声明的CoroutineScope扩展函数,这种函数的可见性被严格限制在MyProducerClass的代码范围内——外部类(比如MyConsumerClass)根本无法访问到这个函数,和协程本身的机制无关,纯粹是Kotlin的作用域规则导致的。
详细解释
类内部扩展函数的可见性
在Kotlin中,如果一个扩展函数被定义在某个类的内部,它就属于该类的成员,只能在该类的内部代码中被调用,或者通过该类的实例作用域(比如with(producerInstance) { ... })访问。MyConsumerClass不在MyProducerClass的作用域内,自然找不到myProducerFunction。协程扩展函数的调用前提(额外说明)
哪怕这个函数是全局可见的,调用myProducerFunction()也需要在一个CoroutineScope的作用域内(因为它是CoroutineScope的扩展函数)——你在runBlocking中是满足这个条件的,但首先的问题是你根本看不到这个函数。
为什么移到同一个类就能运行?
当你把所有协程相关函数移到同一个类后,myProducerFunction的作用域覆盖了类内部的所有函数,包括functionToBeCalledByApplication中的runBlocking代码块,所以能正常调用。
解决方案
根据你的需求,有几种常用的修复方式:
方案1:将扩展函数改为顶层函数
把myProducerFunction移到任何类的外部,变成全局可见的CoroutineScope扩展函数:
// 顶层扩展函数,全局可见 fun CoroutineScope.myProducerFunction(): ReceiveChannel<someClass> = produce { // send some stuff to a channel } class MyProducerClass { // 不再需要内部定义该函数 } class MyConsumerClass { fun CoroutineScope.myConsumerFunction(channel: ReceiveChannel<someClass>) = launch { // Consume stuff from the channel } fun functionToBeCalledByApplication() = runBlocking { myConsumerFunction(myProducerFunction()) // 现在可以正常访问 } }
方案2:将扩展函数改为类的成员函数
把myProducerFunction改成MyProducerClass的普通成员函数,接收CoroutineScope作为参数:
class MyProducerClass { fun myProducerFunction(scope: CoroutineScope): ReceiveChannel<someClass> = scope.produce { // send some stuff to a channel } } class MyConsumerClass { fun CoroutineScope.myConsumerFunction(channel: ReceiveChannel<someClass>) = launch { // Consume stuff from the channel } fun functionToBeCalledByApplication() = runBlocking { val producer = MyProducerClass() myConsumerFunction(producer.myProducerFunction(this)) // 传入当前CoroutineScope } }
方案3:通过伴生对象暴露扩展函数
如果希望保留函数在MyProducerClass内部,可以把它放在伴生对象中,让外部通过类名访问:
class MyProducerClass { companion object { fun CoroutineScope.myProducerFunction(): ReceiveChannel<someClass> = produce { // send some stuff to a channel } } } class MyConsumerClass { fun CoroutineScope.myConsumerFunction(channel: ReceiveChannel<someClass>) = launch { // Consume stuff from the channel } fun functionToBeCalledByApplication() = runBlocking { myConsumerFunction(MyProducerClass.myProducerFunction()) // 通过伴生对象访问 } }
内容的提问来源于stack exchange,提问作者vidul_mahendru

