0

我正在调查 Kotlin 密封类。

我希望强制我所有的密封类具有 NO_OP 的标准值。

例如:-

密封类 1

sealed class Operation {
    object NO_OP: Operation()
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()
}

密封 2 级

sealed class ScreenState {
    object NO_OP: ScreenState()
    class Error : ScreenState()
    class Loading : ScreenState()
    data class Data(val someData: SomeData) : ScreenState()
}

有什么方法可以强制单独文件中的所有密封类始终指定 NO_OP 值?

4

2 回答 2

2

这在目前的语言中似乎是不可能的。但是,我们可以NO_OP从密封的层次结构中移出并使用Coproduct箭头库来定义一个特别的密封层次结构:

import arrow.generic.coproduct2.Coproduct2

sealed class Operation {
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()
}

object NO_OP

typealias Operations = Coproduct2<NO_OP, Operation>

或者我们可以修复 中的类型参数之一Coproduct,并具有:

typealias SomeThingWithNoOp<T> = Coproduct<NO_OP, T>

但是,这并不理想,因为它使层次结构嵌套。顶层是Coproduct,嵌套层是自定义层次结构。这可能会在不久的将来通过 Arrow-Meta 的联合类型插件来解决。


另一种看待问题的方法是将NO_OP其视为哨兵值。所以我们可以编码NO_OP为 null 或None(来自箭头选项):

sealed class Operation {
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()
}

typealias Operations = Operation?
import arrow.core.Option

sealed class Operation {
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()
}

typealias Operations = Option<Operation>

根据我的经验,将其编码为可空可能更容易使用,因为 Kotlin 具有内置的可空类型支持。虽然将其编码为OptionCoproductNO_OP在每个密封层次结构中定义会使其更加明显(尤其是通过使用CoproductNO_OP在密封层次结构中)。


参考:

于 2019-11-15T17:36:05.357 回答
2

如果您想将其强制覆盖为某个密封类的字段:

sealed class ScreenState {
    abstract val NO_OP: ScreenState

    class Error : ScreenState() {
       override val NO_OP: ScreenState
          get() = TODO("Your implementation")
    }
}

如果您需要类似接口的东西:

sealed class ScreenState : NoOp<ScreenState> {
    override val NO_OP: ScreenState = Error()

    class Error : ScreenState()
}

interface NoOp<T> {
   val NO_OP: T
}
于 2019-11-15T15:29:40.017 回答