Kotlin 和有区别的联合(求和类型)
Kotlin and discriminated unions (sum types)
Kotlin 是否有类似可区分联合(求和类型)之类的东西?这个 (F#) 的惯用 Kotlin 翻译是什么:
type OrderMessage =
| New of Id: int * Quantity: int
| Cancel of Id: int
let handleMessage msg =
match msg with
| New(id, qty) -> handleNew id qty
| Cancel(id) -> handleCxl id
在 OO 语言(例如 Kotlin 或 Scala)中实现这种抽象的常见方法是通过继承:
open class OrderMessage private () { // private constructor to prevent creating more subclasses outside
class New(val id: Int, val quantity: Int) : OrderMessage()
class Cancel(val id: Int) : OrderMessage()
}
如果你愿意,你可以将公共部分推到超类中:
open class OrderMessage private (val id: Int) { // private constructor to prevent creating more subclasses outside
class New(id: Int, val quantity: Int) : OrderMessage(id)
class Cancel(id: Int) : OrderMessage(id)
}
类型检查器不知道这样的层次结构是封闭的,所以当你在上面做类似大小写的匹配(when
-表达式)时,它会抱怨它不是详尽无遗的,但是这将很快得到解决。
更新: 虽然 Kotlin 不支持 模式匹配 ,但您可以使用 when-表达式作为智能转换以获得几乎相同的行为:
when (message) {
is New -> println("new $id: $quantity")
is Cancel -> println("cancel $id")
}
查看有关智能转换的更多信息here。
Kotlin's sealed class
approach to that problem is extremely similar to the Scala sealed class
and sealed trait
.
示例(取自链接的 Kotlin 文章):
sealed class Expr {
class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
Kotlin 中的 sealed class 被设计为能够表示总和类型,就像 Scala 中的 sealed 特征一样。
示例:
sealed class OrderStatus {
object Approved: OrderStatus()
class Rejected(val reason: String): OrderStatus()
}
当您在匹配的 when 表达式中使用密封 classes 时,使用密封的 classes 的主要好处就会发挥作用。
如果可以验证该语句涵盖所有情况,则无需在语句中添加 else 子句。
private fun getOrderNotification(orderStatus:OrderStatus): String{
return when(orderStatus) {
is OrderStatus.Approved -> "The order has been approved"
is OrderStatus.Rejected -> "The order has been rejected. Reason:" + orderStatus.reason
}
}
有几点需要注意:
在Kotlin中执行smartcast时,这意味着在这个例子中不需要执行从OrderStatus到OrderStatus.Rejected的转换来访问原因属性.
如果我们没有定义如何处理被拒绝的案例,编译就会失败,并且在 IDE 中会出现这样的警告:
'when' 表达式必须详尽,添加必要的 'is Rejected' 分支或 'else' 分支。
- 何时可以用作表达式或语句。如果将其用作表达式,则满足分支的值将成为通用表达式的值。如果用作语句,则忽略各个分支的值。这意味着缺少分支的情况下的编译错误仅在将其用作表达式时发生,使用结果。
这是我博客(西班牙语)的 link,其中我有一篇更完整的关于 ADT 和 kotlin 示例的文章:http://xurxodev.com/tipos-de-datos-algebraicos/
一个人会做这样的事情:
sealed class Either<out A, out B>
class L<A>(val value: A) : Either<A, Nothing>()
class R<B>(val value: B) : Either<Nothing, B>()
fun main() {
val x = if (condition()) {
L(0)
} else {
R("")
}
use(x)
}
fun use(x: Either<Int, String>) = when (x) {
is L -> println("It's a number: ${x.value}")
is R -> println("It's a string: ${x.value}")
}
Kotlin 是否有类似可区分联合(求和类型)之类的东西?这个 (F#) 的惯用 Kotlin 翻译是什么:
type OrderMessage =
| New of Id: int * Quantity: int
| Cancel of Id: int
let handleMessage msg =
match msg with
| New(id, qty) -> handleNew id qty
| Cancel(id) -> handleCxl id
在 OO 语言(例如 Kotlin 或 Scala)中实现这种抽象的常见方法是通过继承:
open class OrderMessage private () { // private constructor to prevent creating more subclasses outside
class New(val id: Int, val quantity: Int) : OrderMessage()
class Cancel(val id: Int) : OrderMessage()
}
如果你愿意,你可以将公共部分推到超类中:
open class OrderMessage private (val id: Int) { // private constructor to prevent creating more subclasses outside
class New(id: Int, val quantity: Int) : OrderMessage(id)
class Cancel(id: Int) : OrderMessage(id)
}
类型检查器不知道这样的层次结构是封闭的,所以当你在上面做类似大小写的匹配(when
-表达式)时,它会抱怨它不是详尽无遗的,但是这将很快得到解决。
更新: 虽然 Kotlin 不支持 模式匹配 ,但您可以使用 when-表达式作为智能转换以获得几乎相同的行为:
when (message) {
is New -> println("new $id: $quantity")
is Cancel -> println("cancel $id")
}
查看有关智能转换的更多信息here。
Kotlin's sealed class
approach to that problem is extremely similar to the Scala sealed class
and sealed trait
.
示例(取自链接的 Kotlin 文章):
sealed class Expr {
class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
Kotlin 中的 sealed class 被设计为能够表示总和类型,就像 Scala 中的 sealed 特征一样。
示例:
sealed class OrderStatus {
object Approved: OrderStatus()
class Rejected(val reason: String): OrderStatus()
}
当您在匹配的 when 表达式中使用密封 classes 时,使用密封的 classes 的主要好处就会发挥作用。
如果可以验证该语句涵盖所有情况,则无需在语句中添加 else 子句。
private fun getOrderNotification(orderStatus:OrderStatus): String{
return when(orderStatus) {
is OrderStatus.Approved -> "The order has been approved"
is OrderStatus.Rejected -> "The order has been rejected. Reason:" + orderStatus.reason
}
}
有几点需要注意:
在Kotlin中执行smartcast时,这意味着在这个例子中不需要执行从OrderStatus到OrderStatus.Rejected的转换来访问原因属性.
如果我们没有定义如何处理被拒绝的案例,编译就会失败,并且在 IDE 中会出现这样的警告:
'when' 表达式必须详尽,添加必要的 'is Rejected' 分支或 'else' 分支。
- 何时可以用作表达式或语句。如果将其用作表达式,则满足分支的值将成为通用表达式的值。如果用作语句,则忽略各个分支的值。这意味着缺少分支的情况下的编译错误仅在将其用作表达式时发生,使用结果。
这是我博客(西班牙语)的 link,其中我有一篇更完整的关于 ADT 和 kotlin 示例的文章:http://xurxodev.com/tipos-de-datos-algebraicos/
一个人会做这样的事情:
sealed class Either<out A, out B>
class L<A>(val value: A) : Either<A, Nothing>()
class R<B>(val value: B) : Either<Nothing, B>()
fun main() {
val x = if (condition()) {
L(0)
} else {
R("")
}
use(x)
}
fun use(x: Either<Int, String>) = when (x) {
is L -> println("It's a number: ${x.value}")
is R -> println("It's a string: ${x.value}")
}