《Kotlin核心编程》 ——2.4 面向表达式编程

举报
华章计算机 发表于 2020/02/21 20:54:08 2020/02/21
【摘要】 本节书摘来自华章计算机《Kotlin核心编程》 —— 书中第2章,第2.4.1节,作者是水滴技术团队 。

2.4 面向表达式编程

在本章之前的几节中,我们已经好几次与一个关键字打过交道,这就是“表达式”。现在罗列下我们已经提及的表达式概念:

if表达式

函数体表达式

Lambda表达式

函数引用表达式

显然,表达式在Kotlin这门语言中处于一个相当重要的地位,这一节我们会着重介绍在Kotlin中如何利用各种表达式来增强程序表达、流程控制的能力。与Java等语言稍显不同的是,Kotlin中的流程控制不再是清一色的普通语句,它们可以返回值,是一些崭新的表达式语句,如if表达式、when表达式、try表达式等。这样的设计自然与表达式自身的特质相关。在了解具体的语法之前,我们先来探究下表达式和普通语句之间的区别。

表达式(expressions)和语句(statements)虽然是很基本的概念,但也经常被混淆和误解。语句很容易理解,我们在一开始学习命令式编程的时候,程序往往是由一个个语句组成的。比如以下这个例子:

fun main(args: Array<String>) {

    var a = 1

    while (a < 10) {

        println(a)

        a++

    }

}

可以看到,该程序依次进行了赋值、循环控制、打印等操作,这些都可以被称为语句。我们再来看看什么是表达式:

表达式可以是一个值、常量、变量、操作符、函数,或它们之间的组合,编程语言对其进行解释和计算,以求产生另一个值。

通俗地理解,表达式就是可以返回值的语句。我们来写几个表达式的例子:

1 // 单纯的字面量表达式,值为1

-1 // 增加前缀操作符,值为-1

1 + 1 // 加法操作符,返回2

listOf(1, 2, 3) //列表表达式

"kotlin".length // 值为6

这些都是非常明显的表达式。以下是Kotlin中更复杂的表达式例子:

{ x: Int -> x + 1  }       // Lambda表达式,类型为(Int) -> Int

fun(x: Int) { println(x) } // 匿名函数表达式,类型为(Int) -> Unit

if ( x > 1) x else 1       // if-else表达式,类型为Int,假设x已赋值

正如我们所言,一些在其他语言中的普通语句,在Kotlin中也可以是表达式。这样设计到底有什么好处呢?

2.4.1 表达式比语句更安全

我们先来写一段Java代码。刚开始我们还是采用熟悉的if语句用法:

void ifStatement(Boolean flag) {

    String a = null;

    if (flag) {

        a = "dive into kotlin";

    }

    System.out.println(a.toUpperCase());

}

非常简单的代码,由于if在这里不是一个表达式,所以我们只能够在外部对变量a进行声明。仔细思考一下,这段代码存在潜在的问题:

a必须在if语句外部声明,它被初始化为null。这里的if语句的作用就是对a进行赋值,这是一个副作用。在这个例子中,我们忽略了else分支,如果flag的条件判断永远为true,那么程序运行并不会出错;否则,将会出现“java.lang.NullPointerException”的错误,即使程序依旧会编译通过。因此,这种通过语句创建副作用的方式很容易引发bug。

继续思考,现在的逻辑虽然简单,然而如果变量a来自上下文其他更远的地方,那么这种危险会更加容易被忽视。典型的例子就是一段并发控制的程序,业务开发会变得非常不安全。

接下来,我们再来创建一个Kotlin的版本,现在if会被作为表达式来使用:

fun ifExpression(flag: Boolean) {

    val a = if (flag) "dive into Kotlin" else ""

    println(a.toUpperCase())

}

下面分析Kotlin的版本:

表达式让代码变得更加紧凑了。我们可以把赋值语句与if表达式混合使用,就不存在变量a没有初始值的情况。

在if作为表达式时,else分支也必须被考虑,这很容易理解,因为表达式具备类型信息,最终它的类型就是if、else多个分支类型的相同类型或公共父类型。

可以看出,基于表达式的方案彻底消除了副作用,让程序变得更加安全。当然,这并不是说表达式不会有副作用,实际上我们当然可以用表达式写出带有副作用的语句,就像这样子:

var a = 1

fun foo() = if (a > 0) {

    a = 2 // 副作用,a的值变化了

    a

} else 0

然而从设计角度而言,语句的作用就是服务于创建副作用的,相比较表达式的目的则是为了创造新值。在函数式编程中,原则上表达式是不允许包含副作用的。

一切皆表达式

撇开Haskell不谈,在一些极力支持函数式编程的语言中,比如Scala和F#,即使它们不是纯函数式语言,也都实现了一个特性,即一切皆表达式。一切皆表达式的设计让开发者在设计业务时,促进了避免创造副作用的逻辑设计,从而让程序变得更加安全。

由于把百分之百兼容Java作为设计目标,Kotlin并没有采纳一切皆表达式的设计,然而它在Java的基础上也在很大程度上增强了这一点。正如另一个接下来要提及的例子,就是Kotlin中的函数。与Java的函数不同,Kotlin中所有的函数调用也都是表达式。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。