0.前言
简单的知识点记录而已,参考书籍《Kotlin核心编程》
1.不一样的类型声明
声明变量,类型在变量的后面,如下:
val a: String = "Im a Kotlin"
// 等价写法,省略类型声明,也就是下面1.1中的类型推导
val a = "Im a Kotlin"
1.1.增强的类型推导
编译器可以在不显示声明类型的情况在,自动推导出需要的类型,如下:
val svalue = "Im a Kotlin" // java.lang.String
val ivalue = 123 // int
val lvalue = 123L // long
val fvalue = 123F // float
val dvalue = 123.12 // double
上述可以在REPL中打印这些变量的类型,如 println(svalue.javaClass.name) 。
类型推导提高Kotlin这种静态类型语言的开发效率。
1.2.声明函数返回值类型
虽然Kotlin支持类型推导,但不代表可以不声明函数的返回值类型。
先看看如何用关键字 fun 定义一个函数, 如下:
fun sum(x: Int, y: Int): Int {
return x + y
}
与声明变量一样,类型信息放在函数的后面,如果我们把返回的类型声明去掉就会报错,如下:
// 编译报错:Type mismatch: inferred type is Int but Unit was expected
fun sum(x: Int, y: Int) {
return x + y
}
因为没有声明返回值的类型,函数默认为函数返回 Unit(相当于Java的 void ) 类型,但是方法体中实际 return 了 Int 类型,所以编译报错了。这种情况必须声明返回值类型。
上面的函数还可以进一步增强写法,可以把函数体的 {} 去掉,用等号 = 定义函数,如下:
fun sum(x: Int, y: Int): Int = x + y
// 等价于,去掉了返回值类型
fun sum(x: Int, y: Int) = x + y
这种用单行表达式和等号的语法定义的函数,叫作表达式函数。普通的函数声明叫作代码块函数。
在使用表达式函数定义的情况下,可以不声明函数的返回值类型。 但是如果递归,还是要声明返回值类型,如下:
// 没有返回值类型,会报错:Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
fun foo(n: Int) = if (n == 0) 1 else n * foo(n - 1)
// 正确写法,加上返回值类型的声明
fun foo(n: Int): Int = if (n == 0) 1 else n * foo(n - 1)
编译器不能针对递归函数的情况推导类型。
1.3.是否需要显示声明类型?
- 如果是一个函数的参数:必须声明(参数类型)。
- 一个非表达式函数:那就是代码块函数,除非函数没有返回(Unit),否则必须声明(返回值类型的)。
- 递归函数:必须声明(返回值类型)。
- 其他情况:建议声明,增强代码的可读性。
2.val和var的使用规则
Kotlin声明变量使用 var 和 val 的概念。
- var相当于Java中的变量,可变的。
- val相当于Java中的 final 修饰的变量,不可变的(引用不可变)。
2.1.val的含义:引用不可变
val声明的变量的引用不可变,但是引用对象可以变,如下:
// val定义一个数组
val x = intArrayOf(1,2,3)
// 修改数组的引用,会报错,因为x是val修饰,引用不可变,不能指向另一个数组
x = intArrayOf(2,3,4)
// 修改引用对象的某个值,可以,结果是[6,2,3]
x[0] = 6
因此 val 声明的变量是只读变量,它的引用不可变,但是引用的对象(的可变成员)可以变。如下:
// 这里Book类的参数使用var声明
class Book(var name: String){
fun printName(): Unit {
println("这本书的名字是:$name")
}
}
// 创建对象,打印name
val book: Book = Book("Kotlin核心编程")
book.printName()
// 修改Book的name的值,不报错,可以正常打印
book.name = "Java核心编程"
book.printName()
执行结果:
这本书的名字是:Kotlin核心编程
这本书的名字是:Java核心编程
如果把Book的参数 name 的参数类型改为 val,上述就会报错了。
注意,上述示例中类的使用后续会说明,这里只是说明 var 和 val 的区别。
开发中,建议优先使用 val、尽可能采用 val 、不可变对象以及纯函数(没有副作用的函数)来设计程序。
3.高阶函数和Lambda
函数式语言典型特征就是函数是头等公民,不仅可以像类一样在顶层直接定义一个函数,也可以在一个函数内部定义一个局部函数。
此外也可以直接将函数像普通变量一样传递给另一个函数,或者在其他函数内被返回。
3.1.抽象和高阶函数
抽象不多做解释,和Java的抽象一样理解。
高阶函数,可以理解为“把其他函数作为参数或者返回值的函数”,是一种高级的抽象机制,增强语言表达能力。
3.2.函数作为参数的需求
需求:有一堆人员Person的集合数据,有属性name、age和address,需要根据不同条件从集合中筛选人员,就可以把这里的“不同条件筛选”的行为抽象成一个参数,这个参数是一个类对象,也就是多个不同的筛选条件需求创建多个不同的类,这些类各自实现了一个筛选行为。
先定义一个data class声明Person数据类(后面会说明,其实就是相当于Java的POJO对象),如下:
// data class 声明一个Person数据类
data class Person(
val name: String,
val age: Int,
val address: String
)
然后定义一个类,里面有一个方法,筛选符合条件的Person的方法,例如我们想要年龄age≥40岁的人员,如下:
class FilterPersonByAge {
fun ageGe40Filter(person: Person): Boolean {
return person.age >= 40
}
}
现在,如何把上面类 FilterPersonByAge 的方法 ageGe40Filter 变成另一个函数的参数呢?也就是将 ageGe40Filter 变成一个函数类型的参数。
3.3.函数的类型
Kotlin中,函数类型的格式如下:
(Int) -> Unit
如上,Kotlin中的函数类型的声明遵循以下几点:
- 通过 -> 符号来组织函数类型的参数类型和返回值类型,左边是参数类型,右边是返回值类型。
- 必须用一个括号来包裹函数类型的参数类型。如果是一个没有参数的函数类型,参数类型部分用 () 表示,即 () -> Unit
- 返回值类型即使是 Unit,也必须显示声明。
- 如果是多个参数的情况,使用逗号分割参数类型部分,即 (Int, String) -> Unit
- Kotlin还支持为函数类型的参数类型部分指定名字,即 (errCode: Int, errMsg: String) -> Unit
- 高阶函数支持返回另一个函数,即 (Int) -> ((Int) -> Unit),表示传入类型为Int的参数,返回另一个类型为 (Int) -> Unit 的函数,可以简化写法,即 (Int) -> Int -> Unit
所以,现在回到刚才Person筛选的问题,我们还缺少一个通过对Person数据进行筛选的过程方法,如下:
// 接收一个函数类型的参数test,函数的参数是Person类型,返回Boolean类型
fun filterPerson(persons: List<Person>, test: (Person) -> Boolean): List<Person> {
val pList = mutableListOf<Person>()
for (p in persons) {
// 使用函数类型的参数进行筛选
if (test(p)) {
pList.add(p)
}
}
return pList
}
最后,如何将 FilterPersonByAge 类的方法 ageGe40Filter 作为一个参数传递给方法 filterPerson 呢?继续往下看。
3.4.方法和成员引用
Kotlin中,存在语法通过两个冒号来实现对某个类的方法进行引用,所以刚才的问题,我们需要将 FilterPersonByAge 类的方法 ageGe40Filter 作为一个参数传递给方法 filterPerson,就可以这么写,如下:
// 先创建类的对象实例
val filterPersonByAge= FilterPersonByAge()
// 引用方法
filterPersonByAge::ageGe40Filter
到这里,我们涉及到Person的筛选整个过程都完成了,上下文中的完整代码如下:
// 定义一个数据类Person
data class Person (
val name: String,
val age: Int,
val address: String
)
// 创建一个筛选条件的类,实现一个筛选行为
// 不同的筛选条件,定义多个这样的类,但实现的方法都必须是 (Person) -> Boolean 的函数类型
class FilterPersonByAge {
fun ageGe40Filter(person: Person): Boolean {
return person.age >= 40
}
}
// 筛选过程,根据不同的筛选条件test进行筛选
// 接受一个函数类型test的参数
fun filterPerson(persons: List<Person>, test: (Person) -> Boolean): List<Person> {
val pList = mutableListOf<Person>()
for (p in persons) {
// 使用函数类型的参数进行筛选
if (test(p)) {
pList.add(p)
}
}
return pList
}
fun main(){
val p1 = Person("张三", 30, "北京")
val p2 = Person("李四", 40, "上海")
val p3 = Person("王五", 50, "深圳")
val p4 = Person("赵六", 60, "广州")
val allPersonList: List<Person> = listOf(p1, p2, p3, p4)
// 方法引用
val filterPersonByAge = FilterPersonByAge()
val filterPersonList: List<Person> = filterPerson(allPersonList, filterPersonByAge::ageGe40Filter)
for (p in filterPersonList) {
println("筛选人员:${p}")
}
}
执行代码,结果如下:
筛选人员:Person(name=李四, age=40, address=上海)
筛选人员:Person(name=王五, age=50, address=深圳)
筛选人员:Person(name=赵六, age=60, address=广州)
3.5.匿名函数
回顾上述Person筛选的过程,其中类 FilterPersonByAge 的定义不是很好的方案,因为每增加一个筛选条件就要定义一个类似的类,比较麻烦,所以可以使用匿名函数改进优化。
Kotlin支持在缺省函数名的情况在,直接定义一个函数,所以上述代码可以这样改造,取消类 FilterPersonByAge 以及方法 ageGe40Filter 定义,直接执行 filterPerson 方法的时候传递一个匿名函数,如下:
// 直接展示关键代码处改造
...
// 取消定义FilterPersonByAge以及方法引用, 直接传递匿名函数
val filterPersonList: List<Person> = filterPerson(allPersonList, fun(person: Person): Boolean {
return person.age>=40
})
...
是不是更简洁了,其实还有更简单的方法进行优化,这就是Lambda表达式。
3.6.Lambda表达式
Lambda是更简单的概念,可以理解为简化表达后的匿名函数,实质上就是一种语法糖。
上面的 3.5.匿名函数 中,我们使用匿名函数进行了改进,这里分析一下这个匿名函数:
- fun(person: Person)显得比较啰嗦,因为编译器会推导类型,所以只需要一个变量,如 person 就行了。
- 匿名函数体中的关键字 return 也可以省略,这里返回的是一个有值的表达式。
- 模仿函数类型的语法,可以用 -> 把参数和返回值连在一起。
如上,使用Lambda继续改进后就变成如下:
val filterPersonList: List<Person> = filterPerson(allPersonList, {
person -> person.age>=40
})
这就是 Lambda 表达式,和匿名函数一样,是一种函数字面量,接着说明 Lambda 的具体语法。
先用 Lambda 表达式定义一个加法的操作:
// sum是定义的表达式,表达式的参数是两个Int类型,返回值是一个Int类型
val sum: (Int, Int) -> Int = {x: Int, y:Int -> x + y}
因为类型推导,这里可以简化如下:
// 声明了 Lambda 的参数类型,可以不声明 sum的函数类型
val sum = {x: Int, y:Int -> x + y}
// 等价于
// 声明了 sum的函数类型,可以不声明 Lambda 的参数部分类型
val sum: (Int, Int) -> Int = {x, y -> x + y}
如上,总结 Lambda 表达式的语法:
- Lambda 表达式必须通过 {} 来包裹起来。
- 如果 Lambda 声明了参数部分的类型,且返回值类型支持类型推导,那么 Lambda 变量就可以省略函数类型声明。
- 如果 Lambda 声明了函数类型,那么 Lambda 的参数部分的类型可以省略。
- 如果 Lambda 表达式的返回不是 Unit,那么默认最后一行表达式的值类型就是返回值类型。如:
val sum: (Int, Int) -> Int = {x: Int, y:Int ->
val s = x + y
// 返回值是s
s
}
3.7.函数、Lambda
我们很容易对Kotlin的 fun 声明函数、Lambda 表达式的语法产生混淆,因为它们都可以存在花括号 {},现在我们来做个区分:
- fun 在没有等号、只有花括号的情况在,就是我们最常见的代码块函数体,如果返回类型不是 Unit,必须带着 return。如下:
fun foo(x: Int) {
println(x)
}
fun foo(x: Int, y: Int): Int{
return x + y
}
- fun 带有等号,是单表达式函数体,该情况下可以省略 return。如下:
fun foo(x: Int, y: Int) = x + y
- 不管是 val/var 还是 fun,如果同时有等号、花括号的语法,那构建的就是一个 Lambda 表达式,Lambda 的参数在花括号内部声明。所以,如果左侧是 fun,那么就是 Lambda 式函数体,也必须通过 () 或者 invoke 来调用 Lambda,如:
// fun、等号、花括号,定义 Lambda 表达式
fun foo(x: Int, y: Int) = {x + y}
// val、等号、花括号,定义 Lambda 表达式
val foo1 = {x: Int, y: Int -> x + y}
// 调用Lambda表达式 foo
println(foo(1,2)())
println(foo(1,2).invoke())
// 调用Lambda表达式 foo1
println(foo1(2,2))
println(foo1.invoke(2,2))
4.面向表达式编程
4.1.枚举类和when表达式
这里介绍定义枚举结构以及一个非常强大的表达式,也就是when表达式。
下面我们先定义枚举,然后使用枚举进行when的使用来说明用法。
4.1.1.枚举是类
在 Kotlin 中,枚举是通过一个枚举类enum来实现的,如下:
enum class Day{
MON,
TUE,
WED,
THU,
FRI,
SAT,
SUN
}
和Java类似,不同的是枚举类的定义多了一个 class 关键字,表示这是一个枚举类。
但是 Kotlin 的枚举类不简单,由于是一个类,自然也拥有构造函数,以及定义额外的属性和方法,如下:
enum class Day(val day: Int){
MON(1),
TUE(2),
WED(3),
THU(4),
FRI(5),
SAT(6),
// 注意这里结尾有一个分号,为了和下面的方法或者属性定义区分开
SUN(7);
fun getDayNumber(): Int {
return day
}
}
需要注意,枚举类中如果存在额外的方法或者属性定义,必须在枚举项的最后一个结尾处追加分号,这是必须的。
4.1.2.用when代替if-else
上看了解了如何定义一个枚举类,现在我们使用枚举模拟业务,例如,给一周的几天计划了不同的活动,安排如下:
- 周六周日打蓝球
- 周五约会
- 平时里如果晴天就学习,否则就睡觉
设计代码如下,利用一个函数并结合刚才的枚举进行表示:
fun schedule(day: Day, sunny: Boolean){
if (day == Day.SAT && day == Day.SUN) {
// 打篮球
} else if (day == Day.FRI) {
// 约会
} else {
if (sunny) {
// 学习
} else {
// 睡觉
}
}
}
看起来很麻烦,存在了不少的if-else分支,更好的改进方案就是使用when表达式,如下:
fun schedule(day: Day, sunny: Boolean) = when(day) {
Day.SAT,Day.SUN -> "打篮球"
Day.FRI -> "约会"
else -> when {
sunny -> "学习"
else -> "睡觉"
}
}
是不是简单了很多。
4.1.3.when表达式具体语法
具体语法如下:
- 一个完成的 when 表达式类似 switch 语句,由 when 关键字开始,用花括号包含多个逻辑分支,每个分支由 -> 连接,不在需要 switch 的 break 关键字了,由上到下匹配,一直匹配完为止,否则执行 else 分支的逻辑,类似 switch 的 default。
- 每个逻辑分支具有返回值,最终整个 when 表达式的返回类型就是所有分支相同的返回类型,或公共的父类型。
- when 关键字的参数可以省略,如上面的例子中,else 分支中的 when 表达式就没有参数,只不过该情况下,分支 -> 的左侧部分需要返回布尔值(when没有参数的时候),否则编译报错。
- 表达式可以组合,上面的例子中,when 表达式中的 else 分支中还有一个 when,也就是嵌套了一层,可以如下修改:
// when的参数省略,里面的每一行表达式按照3中的要求,左侧是布尔值
fun schedule2(day: Day, sunny: Boolean): String = when {
day == Day.SAT || day == Day.SUN -> "打篮球"
day == Day.FRI -> "约会"
sunny -> "学习"
else -> "睡觉"
}
when是不是很优雅,其实when的实际使用中威力不仅如此,这里不多说明,后续会说明。
4.2.for循环和范围表达式
这里说明 for 循环的语法和引用。
4.2.1.for循环
在Java中,一个 for 循环的基本写法如下:
for (int i=1; i<=10; i++) {
//
}
Kotlin中,for 循环的表达式更简洁,如下:
for (i in 1..10) {
println(i)
}
// 等价于
for (i: Int in 1..10) {
println(i)
}
此外,for 循环中我们还可以通过调用一个 withIndex 方法,提供一个键值元组:
val a = intArrayOf(1,2,3)
for ((index, value) in a.withIndex()) {
println("下标:$index, 数值:$value")
}
4.2.2.范围表达式
上述的 for 循环中的 1…10 这种语法,就是范围表达式(range),Range 表达式是通过 rangeTo 函数实现的,通过 “…” 操作符和某种类型的对象组成,除了整型的基本类型外,该类型需要实现 java.lang.Comparable 接口。
另外,当对整数进行 for 循环时,Kotlin 还提供了一个 step 函数来定义迭代的步长,如下:
// 打印结果是 1,3,5,7,9
for (i in 1..10 step 2) {
println(i)
}
如果是倒序呢?可以使用 downTo 方法实现:
// 打印结果是 10,8,6,4,2
for (i in 10 downTo 1 step 2 ) {
println(i)
}
另外还有一个 until 函数实现一个半开区间,如下:
// 打印结果是 1,2,3,4,5,6,7,8,9 并不包含10
for (i in 1 until 10 ) {
println(i)
}
4.2.3.用in检查成员关系
in 关键字,在 Kotlin 中还可以用来检查一个元素是否是另一个区间或集合中的成员,举例如下:
// 结果是 true
"a" in listOf<String>("b","c","d","a")
如果在 in 前面加上感叹号,那就是相反的判断结果:
// 结果是 false
"a" !in listOf<String>("b","c","d","a")
in 还可以结合范围表达式来表示更多的含义:
4.2.4.函数可变参数
Kotlin 通过关键字 vararg 来定义函数中的可变参数,类似 Java 中的 “…” 的效果。
需要注意,Java 中的可变参数必须位于方法的最后一个参数,Kotlin 可没有这个限制(不过调用的时候有点不同)。
同样的,Kotlin 的可变参数在函数体中也是以数组的方式来使用可变参数的,如下:
// 可变参数names位于函数第一个参数
fun printlnNames(vararg names: String, msg: String) {
println(msg)
for (name in names) {
println(name)
}
}
// 调用的时候,第二个参数通过指定参数名区分开可变参数
printlnNames("张三", "李四", "王五", msg = "下面有请运动员入场:")
如果可变参数位于函数最后一个,那就可以不指定参数名了,如下:
fun printlnNames( msg: String, vararg names: String) {
println(msg)
for (name in names) {
println(name)
}
}
printlnNames("下面有请运动员入场:","张三", "李四", "王五")
此外,还可以使用星号(*)来传入函数外部的变量作为可变参数的变量,如下:
fun printlnNames( msg: String, vararg names: String) {
println(msg)
for (name in names) {
println(name)
}
}
val namearr = arrayOf("张三", "李四", "王五")
printlnNames("下面有请运动员入场:", *namearr)
4.3.中缀表达式
上面我们已经见识到了不少 Kotlin 中的奇特用法,例如 in、step、downTo、until,它们可以不通过点号,而是通过中缀表达式来调用,从而更简洁直观,这些是如何实现的呢?
先看 Kotlin 标准库中另一个类似的方法 to 的设计,这是一个通过泛型实现的方法,可以返回一个 Pair,如下:
public infix fun <A, B> A.to(that: B): Pair<A, B>
在 Kotlin 中,to 这种形式定义的函数成为 中缀函数,一个中缀函数的表达形式非常简单,可以理解成这样:
A 中缀方法 B
可以发现,如果我们要定义一个中缀函数,需要满足如下条件:
- 该中缀函数必须是某个类型的扩展函数或者成员方法。
- 该中缀函数只能有一个参数。
- 虽然 Kotlin 的函数参数支持默认值,但是中缀函数不能有默认值,否则以上形式的 B 会缺失,从而对中缀表达式的语义造成破坏。
- 同样,该参数也不能是可变参数,因为需要保持参数始终为1个。
由于 to 会返回 Pair 这种键值对的结构数据,因此经常把它和 Map 结合在一起使用,如下:
mapOf(1 to "one", 2 to "two", 3 to "three")
现在再自定义一个中缀函数,如下:
class Person {
// 使用 infix 修饰
infix fun called(name: String){
println("My name is $name")
}
}
因为方法 called 使用了 infix 修饰,所以这样调用:
val person = Person()
// 结果打印:My name is Kotlin
person called "Kotlin"
同样的,上面也可以使用普通的语法调用这个中缀函数,如下:
// 结果打印:My name is Java
person.called("Java")
上述的完整代码如下:
class Person {
// infix修饰,中缀函数的定义
infix fun called(name: String){
println("My name is $name")
}
}
val person = Person()
// 中缀函数调用
person called "Kotlin"
// 普通语法调用
person.called("Java")
5.字符串的定义和操作
现在开始说明 Kotlin 中的字符串基础语法。
和 Java 一样,也是通过双引号定义字符串,是不可变的对象。
val str = "hello world"
熟悉的操作如下:
// 11
println(str.length)
// hello
println(str.substring(0,5))
// hello world, !!!
println(str + ", !!!")
// hello Kotlin
println(str.replace("world", "Kotlin"))
String 是一个字符序列,所以可以进行遍历:
// 打印结果:HELLO WORLD
for (c in str.uppercase()) {
print(c)
}
还可以访问字符序列的成员
// h
println(str[0])
// h
println(str.first())
// d
println(str.last())
// d
println(str[str.length - 1])
此外,Kotlin 的字符串还有很多丰富的 API,如下:
// true
println("".isEmpty())
// false
println(" ".isEmpty())
// true
println(" ".isBlank())
// ed
str.filter { c -> c in 'a'..'e' }.forEach { c -> print(c) }
更多的字符串的语法,请查阅 Kotlin API 文档
5.1.定义原生字符串
Kotlin 中,支持原生字符串,如下:
val str = """
hello, my name is kotlin,
\n today verry happy, because i am a kotlin programmer.
\n hahaha ...
bye ~~~
"""
println(str)
打印结果如下:
hello, my name is kotlin,
\n today verry happy, because i am a kotlin programmer.
\n hahaha ...
bye ~~~
简而言之,用这种三个双引号定义的字符串,最终的打印格式和代码中呈现的格式一致,不会解释转化为转义字符,例如上面的 \n。
原生字符串如果来描述 HTML,会非常方便。
5.2.字符串模板
先看一个字符串常见的拼接:
fun printlnInfo(name: String, age: Int){
println("my name is " + name + ", age is " + age)
}
一个简单的拼接,用了2个加号,如果多变量就更繁琐了,而 Kotlin 引入了字符串模板改进了这一点,支持将变量植入字符串,高进上述代码,如下:
fun printlnInfo(name: String, age: Int) {
println("my name is ${name}, age is ${age}")
}
通过${name}这种格式将变量传入字符串,最终提高了代码的紧凑性和可读性。
除了上面将变量传入字符串,也可以将表达式传入,并且支持在表达式中使用双引号,如下:
val name = "Kotlin"
val hobby = "2"
val message = "my name is ${name}, my hobby is ${ if(hobby == "1") "篮球" else "足球" }"
println(message)
5.3.字符串判等
Kotlin 中的判等性主要有两种类型:
- 结构相等:通过操作符 == 来判断两个对象的内容是否想等。
- 引用相等:通过操作符 === 来判断两个对象的引用是否一样,与之相反的判断操作符是 !==。如果比较的实在运行时的原始类型,比如 Int,那么 === 判断的效果也等价于 ==。
具体例子检测字符串两种类型的判等性:
var a = "Java"
var b = "Java"
var c = "Kotlin"
var d = "Kot"
var e = "lin"
var f = d + e
// true
println(a == b)
// true
println(a === b)
// true
println(c == f)
// false
println(c === f)
6.总结
6.1.类型推导
Kotlin 拥有比 Java 更强大的类型推导功能,避免了静态语言在编码时需要书写大量类型的弊端。
但这不是万能的,在使用代码块函数体时,必须显示声明返回值类型。
此外一些复杂的情况,例如递归,返回值类型也不能省略。
6.2.变量声明
Kotlin 中使用 var 和 val 声明变量以及一些类的成员属性,代表它们的引用可变性。
开发中优先推荐使用 val。
6.3.函数声明
Kotlin 中,普通的函数分为代码块体和表达式体,前者类似 Java 中定义函数的习惯,后者因为是一个表达式,可以说省略 return 关键字。
6.4.高阶函数
Kotlin 中函数是头等公民,所以程序中可以到处声明函数,也可以作为值传递以及作为另一个函数的返回值。
函数作为参数的时候,需要使用函数引用表达式进行传值。
柯里化是函数作为返回值的一种应用,但是很少使用。
6.5.Lambda表达式
可以当做另一种匿名函数。
6.6.表达式和流程控制
表达式更加安全,更利于组合,且更强的表达能力。
流程控制可以利用 if、when、try、range、中缀等表达式,写出更强大的代码。
for 循环更简洁。
6.7.字符串操作
字符串是不可变对象,有丰富的API。
支持原生字符串、字符串模板等功能。