如果要在Java中列举,我们可能会用Enum,而在Kotlin中,除了Enum以外,还有一個更加强大的工具——Sealed Class。
Enum Class
在Kotlin中的枚举类可以写成:
enum class Size {
S, M, L, XL
}
然后使用when 來判断:
fun getLength(size: Size): Int {
when (size) {
Size.S -> return 10
Size.M -> return 30
Size.L -> return 50
Size.XL -> return 100
}
}
枚举类也可以自带参数:
enum class Color(val rgb: String) {
RED("0xFF0000"),
GREEN("0x00FF00"),
BLUE("0x0000FF")
}
使用看看:
fun main(args: Array<String>) {
val length = getLength(Size.XL)
println("length=$length")
// 100
val hex = Color.RED.rgb
println("hex=$hex")
// 0xFF0000
for (v in Size.values()){
println(v)
}
// S
// M
// L
// XL
}
大guy~94醬。
Sealed Class:Enum Class 2.0
來看看Sealed Class有什么特性吧!
首先,先创建一个名为Operation 的sealed class ,加上execute() 方法,來玩玩看:
sealed class Operation {
class Add(val value: Int) : Operation()
class Subtract(val value: Int) : Operation()
class Multiply(val value: Int) : Operation()
class Divide(val value: Int) : Operation()
}
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
is Operation.Divide -> x / op.value
}
fun main(args: Array<String>) {
val result = execute(5, Operation.Subtract(3))
println("result=$result")
}
sealed class 是个很酷的class,以上面的代码为例:·类别中有4个class,分別是Add 、Subtract 、Multiply 与Divide ,而且
也只能有这4个class。什么意思呢?
让我们用execute() 方法来测试看看Operation 类别所有的枚举情況:
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
is Operation.Divide -> x / op.value
}
如果我们试图将其中一种枚举情況刪除:
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
}
Compiler就会有森77的fu:
‘when’ expression must be exhaustive, add necessary ‘is Divide’ branch or ‘else’ branch instead
翻译:when表达式应该是全面性的,应该要包含「其它」情況
所以我们加入else 代表其他未过滤的枚举情況,就可以解決这个警告:
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
else -> x
}
也就是说,你可以在Sealed Class中定义所有可能的情況,而在搭配when 的使用下,compiler会强迫我们得去注意到这些情況是否都有被处理到,是个非常贴心的设计。
想像一下今天我們要开个API给別人,他可能不会知道一个参数status 的所有种类,但通过Sealed Class与when 的组合,可以强迫这个API的使用者去了解所有的种类,并进行处理。
基本上一個Sealed Class的Subclass预设不带任何参数,它会是个object:
sealed class Operation {
class Add(val value: Int) : Operation()
class Subtract(val value: Int) : Operation()
class Multiply(val value: Int) : Operation()
class Divide(val value: Int) : Operation()
object Increment : Operation()
object Decrement : Operation()
}
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
is Operation.Divide -> x / op.value
is Operation.Increment -> x + 1
is Operation.Decrement -> x - 1
}
fun main(args: Array<String>) {
val result = execute(5, Operation.Increment)
println("result=$result")
}
注意到了吗?当搭配when做使用的時候,参数的Sealed Class subclass需要使用is判断 ,不带参数的Object 不需要使用is判断 :
fun execute(x: Int, op: Operation) = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
is Operation.Divide -> x / op.value
Operation.Increment -> x + 1
Operation.Decrement -> x - 1
}
因为Kotlin中的Object 就是最简单的单例模式,每个相同名称的Object其实 都是一样的,也只会有这么一个。
Sealed class进阶应用
我们可以将要对View 進行操作的功能封裝為一個sealed class(這邊為UiOp ),並在裡面列舉出我們將會進行的動作:
sealed class UiOp {
object Show: UiOp()
object Hide: UiOp()
class TranslateX(val px: Float): UiOp()
class TranslateY(val px: Float): UiOp()
}
fun execute(view: View, op: UiOp) = when (op) {
UiOp.Show -> view.visibility = View.VISIBLE
UiOp.Hide -> view.visibility = View.GONE
is UiOp.TranslateX -> view.translationX = op.px
is UiOp.TranslateY -> view.translationY = op.px
}
覆寫「+」號運算符,提升程式碼的易讀性:
class Ui(val uiOps: List = emptyList()) {
operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}
看一下使用範例:
val ui = Ui() +
UiOp.Show +
UiOp.TranslateX(20f) +
UiOp.TranslateY(40f) +
UiOp.Hide
run(view, ui)
fun run(view: View, ui: Ui) {
ui.uiOps.forEach { execute(view, it) }
}
可以看到我们的方法run() 就是对View 進行操作。
进行什么操作呢?这些操作已经用非常易读的方式建构在ui变数了,藉由适时的使用sealed class,可以方便地让我们进行同一个系列的所有相关操作。
?参考文章:https://carterchen247.medium.com/kotlin使用心得-sealed-class-82eccf890ac0
|