在使用kotlin时,由于掌握的不够牢靠,好多时候也还是Java编程的习惯,浪费了kotlin提供的语言特性,方便性,间接性,在阅读一些Android开源库的时候,由于好多都是kotlin语法编写的,看的比较费劲,还得去查阅kotlin的语法,比较不方便,故把kotlin的语法记录下来,方便查阅温故,巩固自己的基础知识。
变量kotlin 中,变量分为 可变变量(var) 和 不可变变量(val) 两类。
val:不可变引用,对应的是 Java 中的 final 变量;使用 val 声明的变量不能在初始化之后再次赋值。var:可变引用,对应的是 Java 中的非 final 变量;使用 var 声明的变量的值可以被改变。
不可变变量在赋值之后就不能再去改变它的状态了,因此不可变变量可以说是线程安全的,因为它们无法改变,所有线程访问到的对象都是同一个,因此也不需要去做访问控制。开发者应当尽可能地使用不可变变量,这样可以让代码更加接近函数式编程风格。
fun main() { //声明一个整数类型的不可变变量,初始化后intValue的值不能再改变了 val intValue: Int = 100 //声明一个双精度类型的可变变量 var doublevalue: Double = 100.0}
在声明变量时,通常不需要显式指明变量的类型,可以由编译器根据上下文自动推导出来。如果只读变量在声明时没有初始的默认值,则必须指明变量类型,且在使用前必须确保在各个分支条件下变量可以被初始化,否则编译器就会报异常。
数据类型 基本数据类型在 kotlin中,一切都是对象,可以在任何变量上调用其成员函数和属性,并不区分基本数据类型和它的包装类。也就是说kotlin 没有像 Java 中那样的原始基本类型,但 byte、char、integer、float 或者 boolean 等类型仍然有保留,但是全部都作为对象存在。
//在 kotlin 中,int、long、float 等类型仍然存在,但是是作为对象存在的 val intIndex: Int = 100 //等价于,编译器会自动进行类型推导 val intIndex = 100 //数字类型不会自动转型,必须要进行明确的类型转换 val doubleIndex: Double = intIndex.toDouble() //以下代码会提示错误,需要进行明确的类型转换 //val doubleIndex: Double = intIndex val intValue: Int = 1 val longValue: Long = 1 //以下代码会提示错误,因为两者的数据类型不一致,需要转换为同一类型后才能进行比较 //println(intValue == longValue) //Char 不能直接作为数字来处理,需要主动转换 val ch: Char = 'c' val charValue: Int = ch.toInt() //以下代码会提示错误 //val charValue: Int = ch //不支持八进制 //二进制 val value1 = 0b00101 //十六进制 val value2 = 0x123
字符串字符串用 String 类型表示。字符串是不可变的。字符串的元素:使用索引运算符访问: s[i];可以用 for 循环迭代字符串,也可以用 + 来连接字符串。
val str = "hello" println(str[1]) for (c in str) { println(c) } val str1 = str + " world"
kotlin 支持在字符串字面值中引用局部变量,只需要在变量名前加上字符 $ 即可,此外还可以包含用花括号{}括起来的表达式,此时会自动求值并把结果合并到字符串中。
val intValue = 100 //可以直接包含变量 println("intValue value is $intValue") //intValue value is 100 //也可以包含表达式 println("(intValue + 100) value is ${intValue + 100}") //(intValue + 100) value is 200
如果你需要在原始字符串中表示字面值($)字符(它不支持反斜杠转义),可以用下列语法:
val price = "${'$'}100.99" println(price) //$100.99
数组数组在 Kotlin 中使用 Array 类来表示,它定义了 get 与 set 函数(按照运算符重载约定这会转变为 [])以及 size 属性,以及一些其他有用的成员函数:
arrayOf():使用库函数 arrayOf() 来创建一个数组并传递元素值给它,如: arrayOf(1, 2, 3) 就创建了 [1, 2, 3]。arrayOfNulls:库函数 arrayOfNulls() 可以用于创建一个指定大小的、初始化所有元素都为空的数组。Array:Array是接受数组大小以及一个函数参数的构造函数,用作参数的函数能够返回给定索引的每个元素初始值,如下所示:// 创建一个 Array
数组类型的类型参数如上面的Array,始终会变成对象类型,因此声明 Array< Int > 将是一个包含装箱类型(java.lang.Integer)的数组,如果想要创建没有装箱的基本数据类型的数组,必须使用一个基本数据类型数组的特殊类IntArray、ByteArray、BooleanArray 等,这些类与 Array 并没有继承关系,但是它们有同样的方法属性集,它们也都有相应的工厂方法。
val x: IntArray = intArrayOf(1, 2, 3)x[0] = x[1] + x[2]// 大小为 5、值为 [0, 0, 0, 0, 0] 的整型数组val arr = IntArray(5)// 例如:用常量初始化数组中的值// 大小为 5、值为 [42, 42, 42, 42, 42] 的整型数组val arr = IntArray(5) { 42 }// 例如:使用 lambda 表达式初始化数组中的值// 大小为 5、值为 [0, 1, 2, 3, 4] 的整型数组(值初始化为其索引值)var arr = IntArray(5) { it * 1 }
集合kotlin中集合分为只读集合与可变集合,如下所示:
List是一个有序集合,可通过索引访问元素。元素可以在 list 中出现多次。Set是唯一元素的集合,一组无重复的对象。一般来说 set 中元素的顺序并不重要。例如,字母表是字母的集合(set)。Map是一组键值对。键是唯一的,每个键都刚好映射到一个值。值可以重复 只读集合的可变性
只读集合不一定就是不可变的。例如,假设存在一个拥有只读类型接口的对象,该对象存在两个不同的引用,一个只读,一个可变,当可变引用修改了该对象后,这对只读引用来说就相当于“只读集合被修改了”,因此只读集合并不总是线程安全的。如果需要在多线程环境下处理数据,需要保证正确地同步了对数据的访问,或者使用支持并发访问的数据结构
例如,list1 和 list1 引用到同一个集合对象, list3 对集合的修改同时会影响到 list1
val list1: List
集合的可空性可以分为三种:
可以包含为 null 的集合元素集合本身可以为 null集合本身可以为 null,且可以包含为 null 的集合元素例如,intList1 可以包含为 null 的集合元素,但集合本身不能指向 null;intList2 不可以包含为 null 的集合元素,但集合本身可以指向 null;intList3 可以包含为 null 的集合元素,且集合本身能指向 null
//List
Any:Any 类型是 kotlin 所有非空类型的超类型,包括像 Int 这样的基本数据类型,如果把基本数据类型的值赋给 Any 类型的变量,则会自动装箱为java.lang.Integer的。Any?:Any?类型是 kotlin 所有可空类型的超类型,如果想要使变量可以存储包括 null 在内的所有可能的值,则需要使用 Any?Unit:Unit 类型类似于 Java 中的 void,可以用于函数没有返回值时的情况,如果函数返回值为 Unit,则可以省略该声明,Unit也可以作为类型参数来声明变量。Nothing:Nothing 类型没有任何值,只有被当做函数返回值使用,或者被当做泛型函数返回值的类型参数使用时才会有意义 函数
kotlin 中的函数以关键字 fun 作为开头,函数名称紧随其后,再之后是用括号包裹起来的参数列表,如果函数有返回值,则再加上返回值类型,用一个冒号与参数列表隔开。
//fun 用于表示声明一个函数,double 是函数名,x表示传入参数,Int 表示函数的返回值类型是int型fun double(x: Int): Int { return 2 * x}
还有一种是表达式函数体,它是用单行表达式与等号定义的函数,表达式函数体的返回值类型可以省略,返回值类型可以自动推断的。如:fun double(x: Int) = x * 2
如果函数没有有意义的返回值,则可以声明为 Unit ,也可以省略 Unit,下面的三种写法是等价的:
fun test(str: String, int: Int): Unit { println(str.length + int) } fun test(str: String, int: Int) { println(str.length + int) } fun test(str: String, int: Int) = println(str.length + int)
函数的参数 命名参数kotlin 允许我们使用命名参数,即在调用某函数的时候,可以将函数参数名一起标明,从而明确地表达该参数的含义与作用,但是在指定了一个参数的名称后,之后的所有参数都需要标明名称。如下所示:
fun main() { //该写法是错误的,在指定了一个参数的名称后,之后的所有参数都需要标明名称 compute(index = 110, "hello") compute(index = 120, value = "hello") compute(130, value = "hello")}fun compute(index: Int, value: String) {}
默认参数函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量。
fun main() { compute(24) compute(24, "world")}fun compute(age: Int, name: String = "hello") {}
有默认参数时,可以省略的只有排在末尾的参数,其他位置的是不能省略的,如下所示:
fun main() { //错误,不能省略参数 name // compute(24) // compute(24,100) //可以省略参数 value compute("hello", 24)}fun compute(name: String = "hello", age: Int, value: Int = 100) {}
但是,如果使用命名参数,可以省略任何有默认值的参数,而且也可以按照任意顺序传入需要的参数。
fun main() { compute(age = 24) compute(age = 24, name = "hello") compute(age = 24, value = 90, name = "hello") compute(value = 90, age = 24, name = "hello")}fun compute(name: String = "hello", age: Int, value: Int = 100) {}
可变参数kotlin 的语法与 Java 有所不同,可变参数改为通过使用 varage 关键字声明
fun main() { compute() compute("hello") compute("hello", "world") compute("hello", "world", "kotlin")}fun compute(vararg name: String) { name.forEach { println(it) }}
在 Java 中,可以直接将数组传递给可变参数,而 kotlin 要求显式地解包数组,以便每个数组元素在函数中能够作为单独的参数来调用,这个功能被称为展开运算符,使用方式就是在数组参数前加一个 *
fun main() { val names = arrayOf("hello", "world", "kotlin") compute(* names)}fun compute(vararg name: String) { name.forEach { println(it) }}
局部函数在Kotlin中,支持在函数中嵌套函数,被嵌套的函数称为局部函数
fun compute(name: String, country: String) { fun check(string: String) { if (string.isEmpty()) { throw IllegalArgumentException("参数错误") } } check(name) check(country)}
check方法体是放在compute方法体中,check方法被称为局部方法或局部函数;check只能在compute中方法调用,在compute方法外调用,会引起编译错误。
控制流 IF表达式在 Kotlin 中,if是一个表达式,即它会返回一个值。 因此就不需要三元运算符(条件 ? 然后 : 否则),因为普通的 if 就能胜任这个角色,故kotlin中没有三元运算符。
// 传统用法var max = a if (a < b) max = b// With else var max: Intif (a > b) { max = a} else { max = b} // 作为表达式val max = if (a > b) a else b
if 的分支可以是代码块,最后的表达式作为该块的值
//a,b就是该块儿最后返回的值val max = if (a > b) { print("Choose a") a} else { print("Choose b") b}
如果你使用 if 作为表达式而不是语句(例如:返回它的值或者把它赋给变量),该表达式需要有 else 分支
when表达式when 表达式与 Java 中的 switch/case 类似,但功能更为强大。when 既可以被当做表达式使用也可以被当做语句使用,when 将参数和所有的分支条件顺序比较直到某个分支满足条件,然后它会运行右边的表达式。
如果 when 被当做表达式来使用,符合条件的分支的值就是整个表达式的值。与 Java 的 switch/case 不同之处是 when 表达式的参数可以是任何类型,并且分支也可以是一个条件。
和 if 一样,when 表达式每一个分支可以是一个代码块,它的值是代码块中最后的表达式的值,如果其它分支都不满足条件将会求值于 else 分支。
如果 when 作为一个表达式使用,则必须有 else 分支, 除非编译器能够检测出所有的可能情况都已经覆盖了。如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔。
val value = 2 when (value) { in 4..9 -> println("in 4..9") //区间判断 3 -> println("value is 3") //相等性判断 2, 6 -> println("value is 2 or 6") //多值相等性判断 is Int -> println("is Int") //类型判断 else -> println("else") //如果以上条件都不满足,则执行 else }
fun main() { //返回 when 表达式 fun parser(obj: Any): String = when (obj) { 1 -> "value is 1" "4" -> "value is string 4" is Long -> "value type is long" else -> "unknown" }}
when 语句也可以不带参数来使用
when { 1 > 5 -> println("1 > 5") 3 > 1 -> println("3 > 1") }
For循坏//和java中使用方式很类似 val list = listOf(1, 4, 10, 34, 10) for (value in list) { println(value) }
通过索引来遍历
val items = listOf("H", "e", "l", "l", "o") //通过索引来遍历List for (index in items.indices) { println("${index}对应的值是:${items[index]}") }
也可以在每次循环中获取当前索引和相应的值
val list = listOf(1, 4, 10, 34, 10) for ((index, value) in list.withIndex()) { println("index : $index , value :$value") }
也可以自定义循环区间
for (index in 2..10) { println(index) }
while循环和do/while循环两者的使用和Java中的使用相似。
val list = listOf(1, 4, 15, 2, 4, 10, 0, 9) var index = 0 while (index < list.size) { println(list[index]) index++ }
val list = listOf(1, 4, 15, 2, 4, 10, 0, 9) var index = 0 do { println(list[index]) index++ } while (index < list.size)
返回与跳转Kotlin 有三种结构化跳转表达式(作用和java语言中的类似):
return 默认从最直接包围它的函数或者匿名函数返回break 终止最直接包围它的循环continue 继续下一次最直接包围它的循环
在 kotlin 中任何表达式都可以用标签(label)来标记,标签的格式为标识符后跟 @ 符号,例如:abc@ 、fooBar@ 都是有效的标签
fun main() { fun1()}fun fun1() { val list = listOf(1, 4, 6, 8, 12, 23, 40) loop@ for (it in list) { if (it == 8) { continue } //当值是23的时候,退出标记的loop循环 if (it == 23) { break@loop } println("value is $it") } println("function end")}
通常情况下使用隐式标签更方便, 隐式标签与接受该 lambda 的函数同名,return也可添加标签限制(如下:)
fun fun3() { val list = listOf(1, 4, 6, 8, 12, 23, 40) list.forEach { if (it == 8) { //这是就是在return上加了隐式标签forEach的限制,使之只在forEach当前循环中终止返回,效果同continue return@forEach } println("value is $it") } println("function end") //运行fun3方法的话,会输出以下结果:// value is 1// value is 4// value is 6// value is 12// value is 23// value is 40// function end}fun fun4() { val list = listOf(1, 4, 6, 8, 12, 23, 40) list.forEach loop@{ if (it == 8) { return@loop//同fun3一样,这里是添加了loop的标签显式标签 } println("value is $it") } println("function end")//运行fun4方法的话,会输出以下结果:// value is 1// value is 4// value is 6// value is 12// value is 23// value is 40// function end}fun fun5() { listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) { if (value == 3) { //局部返回到匿名函数的调用者,即 forEach 循环 return } println("value is $value") }) println("function end")}//运行fun5方法的话,会输出以下结果://value is 1//value is 2//value is 4//value is 5//function end
区间Kotlin 可通过调用 kotlin.ranges 包中的 [rangeTo()] 函数及其操作符 .. 形式的轻松地创建两个值的区间。 通常,rangeTo() 会辅以 in 或 !in 函数
if (i in 1..4) { // 等同于 i>=1 && i <= 4 print(i)}if (i in 1.rangeTo(4)) { // 和上面是相同的 print(i)}
数字类型的 ranges 在被迭代时,等同于java中带索引的fori的循环的效果
for (i in 1..4) { print(i)}
要反向迭代数字,请使用 [downTo]函数而不是 .. 或rangeTo()。
for (i in 4 downTo 1) print(i)
这是通过 [step]函数完成任意步长(不一定为 1 )迭代数字
for (i in 1..8 step 2) print(i)println()for (i in 8 downTo 1 step 2) print(i)
以上声明的都是闭区间,如果想声明的是开区间),可以使用 until 函数
for (i in 1 until 4) { println(i)}
空安全在 kotlin 中,类型系统将一个引用分为可以容纳 null (可空引用)或者不能容纳 null(非空引用)两种类型。 常规的变量不能指向null,如果希望一个变量可以储存 null 引用,需要显式地在类型名称后面加上问号?。
//name变量不能被赋值为null var name: String = "hello" //name变量可以被赋值为null var name: String? = "hello"
kotlin 对可空类型的显式支持有助于防止 NullPointerException 导致的异常问题,编译器不允许不对可空变量做 null 检查就直接调用其属性。
var name: String? = "hello"val l = name.length // 编译器会报错,因为name变量可能为null
在写代码的时候,我们可以对可空的变量做判空判断,kotlin也为我们提供了安全的调用的运算符
安全的调用?.:如果变量值非空,则变量的方法或属性会被调用,否则直接返回 null。
//b变量声明的是可空类型的,当b!=null的时候,就返回的b.length的值//当b=null时,b?.length就自动返回的null,可以看出这个表达式返回的类型是Int?println(b?.length)
安全调用在链式调用中也很有用,例如,如果一个员工 Bob 可能会(或者不会)分配给一个部门, 并且可能有另外一个员工是该部门的负责人,那么获取 Bob 所在部门负责人(如果有的话)的名字,我们写作:bob?.department?.head?.name
如果任意一个属性(环节)为空,这个链式调用就会返回 null。
如果要只对非空值执行某个操作,安全调用操作符可以与 [let] 一起使用:
val listWithNulls: List
安全调用也可以出现在赋值的左侧。这样,如果调用链中的任何一个接收者为空都会跳过赋值,而右侧的表达式根本不会求值:
// 如果 `person` 或者 `person.department` 其中之一为空,都不会调用该函数:person?.department?.head = managersPool.getManager()
Elvis 操作符当我们有一个可空的引用 b 时,如果 b 非空,我使用它;如果使用 b 是空的话,我们想使用某个非空的值,如下所示:
val l: Int = if (b != null) b.length else -1
除了可以用if表达式,这还可以通过 Elvis 操作符表达,写作 ?:
//当表达式b?.length为空(即b=null)的时候赋值为-1val l = b?.length ?: -1
如果 ?: 左侧表达式非空,elvis 操作符就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧为空时,才会对右侧表达式求值。
安全的类型转换如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException。 另一个选择是使用安全的类型转换as?,如果尝试转换不成功则返回 null:
//如果a不是Int类型的话,会返回nullval aInt: Int? = a as? Int
非空断言运算符!!!!是将任何值转换为非空类型,若该值为空则抛出异常
//如果b=null,会抛出空指针NPE异常val l = b!!.length
可空类型的扩展为可空类型定义扩展函数是一种更强大的处理 null 值的方式,可以允许接收者为 null 的调用,并在该函数中处理 null ,而不是在确保变量不为 null 之后再调用它的方法
//可以被正常调用而不会发生空指针异常 val name: String? = null println(name.isNullOrEmpty()) //true
类型检测与类型转换 类型检查is 与 !is 操作符在运行时通过使用 is 操作符或其否定形式 !is 来检测对象是否符合给定类型
if (obj is String) { print(obj.length)}if (obj !is String) { // 与 !(obj is String) 相同 print("Not a String")}else { print(obj.length)}
智能转换在许多情况下,不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is-检测以及[显式转换],在需要时自动插入(安全的)转换:
fun demo(x: Any) { if (x is String) { print(x.length) // x 自动转换为字符串 }}if (x !is String) returnprint(x.length) // x 自动转换为字符串// `||` 右侧的 x 自动转换为字符串if (x !is String || x.length == 0) return// `&&` 右侧的 x 自动转换为字符串if (x is String && x.length > 0) { print(x.length) // x 自动转换为字符串}//用在when表达式和while循环也是一样when (x) { is Int -> print(x + 1) is String -> print(x.length + 1) is IntArray -> print(x.sum())}
不安全转换操作符如果转换是不可能的,转换操作符会抛出一个异常,因此,它是不安全的。 Kotlin 中的不安全转换由操作符 as完成;可空类型和不可空类型是不同的类型,不能转换,比如:String和String?是不同的类型。
安全转换操作符为了避免抛出异常,可以使用安全转换操作符 as? ,它可以在失败时返回 null:
//as?右边是非空类型的,声明是可空类型的,若用as就会抛出异常,这里使用as?可以返回null,所以结果是可空的val x: String? = y as? String
作用域函数kotlin标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个lambda表达式时,他会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称,这些函数称为作用域函数
这些函数基本上做了同样的事情:在一个对象上执行一个代码块。不同的是这个对象在块中如何使用,以及整个表达式的结果是什么。
下面是作用域函数的典型用法:
Person("Alice", 20, "Amsterdam").let { println(it) it.moveTo("London") it.incrementAge() println(it)}
如果不使用 let 来写这段代码,就必须引入一个新变量,并在每次使用它时重复其名称。
val alice = Person("Alice", 20, "Amsterdam")println(alice)alice.moveTo("London")alice.incrementAge()println(alice)
作用域函数没有引入任何新的技术,但是它们可以使你的代码更加简洁易读。
区别标准库中提供了几个作用域函数,他们本质上都非常相似,每个作用域函数之间有两个主要区别:
引用上下文对象的方式返回值 上下文对象:this还是it this
this关键字可看作为lambda 表达式的的接收着。run、with 以及 apply 通过关键字 this 引用上下文对象,在它们的 lambda 表达式中可以像在普通的类函数中一样访问上下文对象。在大多数场景,当你访问接收者对象时你可以省略 this,来让你的代码更简短。
val adam = Person("Adam").apply { age = 20 // 和 this.age = 20 或者 adam.age = 20 一样 city = "London"}println(adam)
itit关键字可看作为作为 lambda 表达式的参数。例如let 及 also 将上下文对象作为 lambda 表达式参数,如果没有指定参数名,对象可以用隐式默认名称 it 访问
fun getRandomInt(): Int { return Random.nextInt(100).also { writeToLog("getRandomInt() generated value $it") }}val i = getRandomInt()
此外,当将上下文对象作为参数传递时,可以为上下文对象指定在作用域内的自定义名称。
fun getRandomInt(): Int { //自定义名称value return Random.nextInt(100).also { value -> writeToLog("getRandomInt() generated value $value") }}val i = getRandomInt()
返回值根据返回结果,作用域函数可以分为以下两类:
apply 及 also 返回上下文对象。let、run 及 with 返回 lambda 表达式结果、返回上下文对象
apply 及 also 的返回值是上下文对象本身。
val numberList = mutableListOf
当然,也还可以用在返回上下文对象的函数的 return 语句中
fun getRandomInt(): Int { return Random.nextInt(100).also { writeToLog("getRandomInt() generated value $it") }}val i = getRandomInt()
返回表达式结果let、run 及 with 返回 lambda 表达式的结果
val numbers = mutableListOf("one", "two", "three")val countEndsWithE = numbers.run { add("four") add("five") count { it.endsWith("e") }//返回的结果}//There are 3 elements that end with e.println("There are $countEndsWithE elements that end with e.")
此外,还可以忽略返回值,仅使用作用域函数为变量创建一个临时作用域。
val numbers = mutableListOf("one", "two", "three")with(numbers) { val firstItem = first() val lastItem = last() println("First item: $firstItem, last item: $lastItem")}
函数选择前文提到了几个函数,那么怎么选择合适的作用域函数呢?,下面是列举了它们之间的主要区别表
一下是对作用域函数的简短总结:
对一个非空(non-null)对象执行 lambda 表达式:let将表达式作为变量引入为局部作用域中:let
fun main() { val nickName = "leavesC" val also = nickName.let { it.length } println(also) //7}
对象配置:apply
val adam = Person("Adam").apply { age = 32 city = "London" }println(adam)
对象配置并且计算结果:run
val service = MultiportService("https://example.kotlinlang.org", 80)val result = service.run { port = 8080 query(prepareRequest() + " to port $port")}
在需要表达式的地方运行语句:非扩展的 run
val hexNumberRegex = run { val digits = "0-9" val hexDigits = "A-Fa-f" val sign = "+-" Regex("[$sign]?[$digits$hexDigits]+")}for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) { println(match.value)}
附加效果:also
val numbers = mutableListOf("one", "two", "three")numbers .also { println("The list elements before adding new one: $it") } .add("four")
一个对象的一组函数调用:with
val result = with(StringBuilder()) { append("leavesC") append("n") for (letter in 'A'..'Z') { append(letter) } toString() } println(result)
takeIf 与 takeUnless 除了作用域函数外,标准库还包含函数 takeIf 及 takeUnless。这俩函数使你可以将对象状态检查嵌入到调用链中。
takeIf 接收一个返回值类型为 bool 的函数,当该参数返回值为 true 时返回接受者对象本身,否则返回 null。
takeUnless 的判断条件与 takeIf 相反。
fun main() { println(check("leavesC")) //7 println(check(null)) //0}fun check(name: String?): Int { return name.takeIf { !it.isNullOrBlank() }?.length ?: 0}