Kotlin 智能类型转换与 when 表达式(八)

打印 上一主题 下一主题

主题 1629|帖子 1629|积分 4887


  • 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类型的属性

        • 这里意味着它们可以是 Num 或 Sum




  1. interface Expr                                         // <1>
  2. class Num(val value: Int) : Expr                       // <2>
  3. 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> 智能转换将类型检查和类型转换合并为一个操作

      • 这里使用"as"语法对Num类型的转换是多余


  1. fun eval(e: Expr): Int {
  2.     if (e is Num) {                           // <1>
  3.         val n = e as Num                      // <2>
  4.         return n.value
  5.     }
  6.     if (e is Sum) {                            // <1>
  7.         return eval(e.right) + eval(e.left)
  8.     }
  9.     throw IllegalArgumentException("Unknown expression")
  10. }
  11. fun main() {
  12.     println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
  13.     // 7
  14. }
复制代码

  • 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 表达式作为函数体


  1. fun eval(e: Expr): Int =            
  2.     if (e is Num) {                  // <1>
  3.         e.value                      // <1>
  4.     } else if (e is Sum) {
  5.         eval(e.right) + eval(e.left)
  6.     }else {
  7.         throw IllegalArgumentException("Unknown expression")
  8.     }
复制代码

  • 你还可以让这段代码更加简便

    • 如果 if 分支中只有一个表达式,那么大括号是可选的
    • 如果一个if分支中有一个代码块,那么最后一个表达式将作为结果返回

  1. fun eval(e: Expr): Int =
  2.     if (e is Num) e.value
  3.     else if (e is Sum)  eval(e.right) + eval(e.left)
  4.     else throw IllegalArgumentException("Unknown expression")
复制代码

  • 使用 when 重写它,这里我们使用不同形式的 when 分支

    • 允许我们检查 when 参数值的类型
    • <1> when分支对参数类型举行检查和智能转换

      • 第一个分支检查通过,则认为e是Num类型,即可以直接访问Num类型的属性
      • 同上,可以直接访问Sum类型的属性,这就是智能转换的强大之处


  1. fun eval(e: Expr): Int =
  2.     when(e) {
  3.         is Num -> e.value                          // <1>
  4.         is Sum -> eval(e.left) + eval(e.right)     // <1>
  5.         else -> throw IllegalArgumentException("Unknown expression")
  6.     }
复制代码
1.0.3 块作为 if 和 when 的分支


  • if 和 when 都可以将代码块作为分支

    • 在这种情况下,块中的最后一个表达式就是结果

  • 假设你想深入了解 eval 函数是如何计算结果的

    • 一种方法是添加 println 语句,记录函数当前正在计算的内容
    • <1> 你可以为 when 表达式中的每个分支在代码块中添加println语句

      • 代码块中的最后一个表达式作为结果返回
      • 在 is Num 分支, e.value 表达式作为结果返回
      • 在 is Sum 分支, left + right 作为结果返回


  1. fun eval(e: Expr): Int =
  2.     when(e) {
  3.         is Num -> {
  4.             println("num: ${e.value}")
  5.             e.value                          // <1>
  6.         }
  7.         is Sum -> {
  8.             val left = eval(e.left)
  9.             val right = eval(e.right)
  10.             println("sum: $left + $right")
  11.             left + right                     // <1>
  12.         }
  13.         else -> throw IllegalArgumentException("Unknown expression")
  14.     }
复制代码

  • 代码块中的最后一个表达式即为结果的规则

    • 实用于所有可以使用代码块期望得到结果的情况

      • 比如上面示例中的 when 分支

    • 在之后学到的异常捕获中,这条规则同样实用于try body和catch子句

      • 也将讨论这条规则在 lambda 表达式中的应用

    • 但正如之前提到的, 这条规则并不实用于常规的函数

      • 函数的代码块体必须显式使用 return 语句
      • 代码块形式的函数与表达式形式的函数



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

反转基因福娃

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表