- when 表达式专题系列
- 从罗列类引出 when 表达式
- 多种形式的 when 表达式
1.0.1 智能转换: 联合类型检查和类型转换
- 将编写一个函数来计算简朴的算术表达式,比如 (1 + 2) + 4
- 这些表达式只包含一种类型的运算: 两个数字之和
- 其他算术运算(如减法、乘法和除法)也可以用类似的方法实现
- 在此过程中,您将了解智能转换
- 如何让处理不同类型的 Kotlin 对象变得更加容易
- 首先,如何对表达式举行编码?
- 传统的做法是将表达式存储在树状结构中
- 其中每个节点要么是一个和(Sum),要么是一个数(Num)
- Num 总是叶子节点,而Sum节点有两个子节点: 求和操作的参数
- <1> 用于编码表达式的简朴类结构
- 一个名为 Expr 的接口, 没有声明任何方法
- 它被用作一个标记接口,为不同类型的表达式提供通用类型
- 实现该接口的两个类 Num 和 Sum
- <2> 要标记一个类实现一个接口
- 可以在类的主构造器后使用冒号(
+接口名
- Num 类实现 Expr 接口并带有一个Int类型的属性
- Sum 类也实现 Expr 接口并带有 left 和 right 两个Expr类型的属性
- interface Expr // <1>
- class Num(val value: Int) : Expr // <2>
- class Sum(val left: Expr, val right: Expr) : Expr // <2>
复制代码
- 比如要存储前面提到的表达式 (1 + 2) + 4
- 需要创建一个 Expr 对象结构,具体为 Sum(Sum(Num(1), Num(2)),Num(4))
- 下图显示该结构的树形表示
- 目标是计算这种由 Sum 和 Num 对象构成的表达式
- Expr 接口有两个实现,因此你必须实验两个选项来评估表达式的结果值
- 如果表达式是数字(Num),则返回相应的值
- 如果是求和(Sum),则必须递归评估左右表达式,并返回它们的和
- 首先,我们将检察该函数的实现,其编写风格类似于 Java 代码
- 然后,我们将对其举行重构,以反映 Kotlin的本土风格
- 最初,您可能会编写一个类似于其他语言风格的函数
- 使用一系列 if 表达式来检查 Expr 的不同子类型
- <1> 在 Kotlin 中,您可以使用 is 检查来检查变量是否属于某种类型
- 如果您使用 C# 编程,可能会对 is 语法感到熟悉
- Java 开辟人员可能会将其等同于 instanceof
- <2> 智能转换将类型检查和类型转换合并为一个操作
- fun eval(e: Expr): Int {
- if (e is Num) { // <1>
- val n = e as Num // <2>
- return n.value
- }
- if (e is Sum) { // <1>
- return eval(e.right) + eval(e.left)
- }
- throw IllegalArgumentException("Unknown expression")
- }
- fun main() {
- println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
- // 7
- }
复制代码
- Kotlin 的 "is"检查提供一些额外的便利
- 如果你检查变量的特定类型,那么之后你就不需要再对它举行显式转换
- 实际上,编译器会为你实行类型转换,我们称之为智能类型转换
- 在 eval 函数中,在检查变量 e 是否具有 Num 类型后
- 编译器会将其智能地表明为 Num 类型的变量
- 然后,您就可以访问 Num 类型的属性,而无需举行显式转换
- 在IntelliJ IDEA和Android Studio中,这些智能转换的值会用背景色凸显
- 因此很容易把握该值是否事先颠末检查
- 同时也可以看到下图中"as Num"背景是灰色, 表示多余
1.0.2 重构: 用 when 表达式更换 if 表达式
- 在前面的学习中,我们已经知道 if 在 Kotlin 中是一个表达式
- 这也是 Kotlin 中没有三元操作符的缘故原由–if 表达式可以返回一个值
- <1> 这意味着你可以重写 eval 函数,使用表达式体语法
- 去掉 return 语句和大括号, 使用 if 表达式作为函数体
- fun eval(e: Expr): Int =
- if (e is Num) { // <1>
- e.value // <1>
- } else if (e is Sum) {
- eval(e.right) + eval(e.left)
- }else {
- throw IllegalArgumentException("Unknown expression")
- }
复制代码
- 你还可以让这段代码更加简便
- 如果 if 分支中只有一个表达式,那么大括号是可选的
- 如果一个if分支中有一个代码块,那么最后一个表达式将作为结果返回
- fun eval(e: Expr): Int =
- if (e is Num) e.value
- else if (e is Sum) eval(e.right) + eval(e.left)
- else throw IllegalArgumentException("Unknown expression")
复制代码
- 使用 when 重写它,这里我们使用不同形式的 when 分支
- 允许我们检查 when 参数值的类型
- <1> when分支对参数类型举行检查和智能转换
- 第一个分支检查通过,则认为e是Num类型,即可以直接访问Num类型的属性
- 同上,可以直接访问Sum类型的属性,这就是智能转换的强大之处
- fun eval(e: Expr): Int =
- when(e) {
- is Num -> e.value // <1>
- is Sum -> eval(e.left) + eval(e.right) // <1>
- else -> throw IllegalArgumentException("Unknown expression")
- }
复制代码 1.0.3 块作为 if 和 when 的分支
- if 和 when 都可以将代码块作为分支
- 假设你想深入了解 eval 函数是如何计算结果的
- 一种方法是添加 println 语句,记录函数当前正在计算的内容
- <1> 你可以为 when 表达式中的每个分支在代码块中添加println语句
- 代码块中的最后一个表达式将作为结果返回
- 在 is Num 分支, e.value 表达式作为结果返回
- 在 is Sum 分支, left + right 作为结果返回
- fun eval(e: Expr): Int =
- when(e) {
- is Num -> {
- println("num: ${e.value}")
- e.value // <1>
- }
- is Sum -> {
- val left = eval(e.left)
- val right = eval(e.right)
- println("sum: $left + $right")
- left + right // <1>
- }
- else -> throw IllegalArgumentException("Unknown expression")
- }
复制代码
- 代码块中的最后一个表达式即为结果的规则
- 实用于所有可以使用代码块并期望得到结果的情况
- 在之后学到的异常捕获中,这条规则同样实用于try body和catch子句
- 但正如之前提到的, 这条规则并不实用于常规的函数
- 函数的代码块体必须显式使用 return 语句
- 代码块形式的函数与表达式形式的函数
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |