Kotlin - 有没有一种方法可以定义函数的显式范围,并在伴随对象中定义接收者?

Kotlin - Is there a way of defining an explicit scope of a function with receiver defined in companion object?

假设您有以下代码:

interface SomeClient {

    fun isValid(value: String): Boolean
}

class SomeData private constructor(
    val value: String
) {

    companion object {

        fun SomeClient.create(value: String): SomeData? =
            if (isValid(value)) {
                SomeData(value)
            } else {
                null
            }
    }
}

class SomeOtherData private constructor(
    val otherValue: String
) {

    companion object {

        fun SomeClient.create(value: String): SomeOtherData? =
            if (isValid(value)) {
                SomeOtherData(value)
            } else {
                null
            }
    }
}

class SomeUseCase(
    someClient: SomeClient
) : SomeClient by someClient {

    fun run() {
        val someData = SomeData.create("hello")
        val someOtherData = SomeOtherData.create("wassup")
    }
}

整个意图是提供一个静态工厂方法来创建有效值对象(SomeDataSomeOtherData),但验证逻辑包括一些 IO 操作。所以我想将创建方法的调用范围限制为实现 SomeClient.

的 类

这里的问题是编译器无法解析SomeUseCase#run内部的伴生对象方法(SomeData#createSomeOtherData#create),它会抱怨接收者不正确。

我当然可以那样做

class SomeUseCase {
    fun run() {
        val someData = this.createSomeData(...)
        val someOtherData = this.createSomeOtherData(...)
    }
}

并相应地重命名创建方法,但我想保留这些名称,所以它是 SomeData.create,而不是 SomeData.createSomeData

有办法实现吗?这有什么意义吗?

当您在 SomeData 伴随对象中编写 fun SomeClient.create(value: String) 时,您并不是在为伴随对象定义 create 方法,而是为 SomeClient 在伴随对象的范围。

如果你重写它使它成为伴随对象的方法,你会看到 isValid() 调用没有接收者,所以它需要作为参数传递到那里:

class SomeData private constructor(val value: String) {
    companion object {
        fun create(value: String, validator: SomeClient): SomeData? =
            if (validator.isValid(value)) SomeData(value) else null
    }
}

之后可以在SomeClient的范围内这样调用:

val someData = SomeData.create("hello", this)

每次创建调用都要重复this有点麻烦,所以你可以在SomeClient接口的范围内为SomeData伴生对象定义一个扩展函数:

interface SomeClient {
    fun isValid(value: String): Boolean

    fun SomeData.Companion.create(value: String) = create(value, this@SomeClient)
}

之后可以在 SomeClient 的范围内以所需的方式调用它:

val someData = SomeData.create("hello")