接着第一课,第一课只说了如何定义函数,几种类型的参数。接下来要说的就是匿名函数,老规矩先上思维导图。
文章目录
【匿名函数与函数类型】
匿名函数
顾名思义定义时不取名字,我们称之为匿名函数,匿名函数通常整体传递给其他函数,或从其他函数返回。
用Kotlin代码理解一下
fun main() {
val count = "MacaoPark".count { letter -> letter == 'a' }
println(count)
}
函数的类型与隐式返回
匿名函数也有类型,匿名函数可以当做变量赋值给函数类型变量,就像其他函数一样匿名函数就可以在代码里传递了。变量有类型,变量可以等于函数,函数也会有类型。 函数的类型,由传入的参数与返回值决定。
和具名函数不同,除了极少情况外,匿名函数不需要return关键字来返回数据,匿名函数会隐式或自动返回函数体最后一行语句的执行结果
val VFunction: () -> String = {
val name = "Han Mei Mei"
"My Name is $name"
}
println(VFunction())
方法名 VFunction 返回值类型是String 匿名函数会隐式或自动返回函数体最后一行语句的执行结果
函数参数
和具名函数一样,可以不带参数也可以带一个或多个类型的参数,需要带参数时,参数的类型放在匿名函数的类型定义中,参数名则放在函数定义中。
val VFunction: (String) -> String = {name ->
"My Name is $name"
}
println(VFunction("Wang Er Hu"))
it 关键字
定义只有一个参数的匿名函数时,可以使用it关键字来表示参数名。当你传入两个值参 it关键字就不能用了。
val VFunction: (String) -> String = {it->
"My Name is $it"
}
println(VFunction("Wang Er Hu"))
类型推断
定义一个变量时,如果已把匿名函数作为变量赋值给它,就不需要指明变量类型了
val VFunction: () -> String = {
val name = "Han Mei Mei"
"My Name is $name"
}
println(VFunction())
推断 ,因为匿名1函数作为变量赋值给它,所以就不需要指明变量的类型了
val VFunction = {
val name = "Han Mei Mei"
"My Name is $name"
}
println(VFunction())
定义的参数是函数的函数
在showOnBoard函数中 传入 discountWords函数
fun main() {
val discountWords = { goodsName: String, hour: Int ->
val currentYear = 2027
"${currentYear}年,双11${goodsName} 促销倒计时:$hour 小时"
}
showOnBoard("卫生纸", discountWords)
}
fun showOnBoard(goodsName: String, discountWords: (String, Int) -> String) {
val hour = (1..24).shuffled().last()
println(discountWords(goodsName, hour))
}
简略写法
如果一个函数的lambda参数排在最后一行,或者是唯一的参数,那么括住的lambda值参的一对括号就可以直接省略
fun main() {
showOnBoard("卫生纸") { goodsName: String, hour: Int ->
val currentYear = 2027
"${currentYear}年,双11${goodsName} 促销倒计时:$hour 小时"
}
}
fun showOnBoard(goodsName: String, discountWords: (String, Int) -> String) {
val hour = (1..24).shuffled().last()
println(discountWords(goodsName, hour))
}
函数内联
lambda可以让你更灵活的编写应用,但是灵活也是要付出代价的。
在JVM上,你定义的lambda会以对象的形式存在,JVM会所有同lambda打交道的变量分配内存,这就产生了内存开销。更糟糕的是,lambda的内存开销会带来严重的性能问题。幸运的是Kotlin 有一种优化机制叫内联,有了内联JVM就不需要lambda对象实例了,因而避免了变量内存分配的问题。
说到内联,就介绍一下内联函数
类的成员依赖于类,只有泛型类才能拥有成员泛型函数,而普通类是不允许定义泛型函数的,否则编译器直接报错。 不过也有例外当参数类都是继承于某种类型,那么允许在定义函数时指定从这个基类泛化开,凡是继承自该基类子类,都可以作为输入参数进行调用,反之无法调用!
举个栗子,Int Float Double 都继承自Number类,但是假如定义一个参数形式为setArrayNumber(array:Array)的函数,它并不接受Array或者Array的入参。如果要让该方法同事接收整型和双精度的数组入参,那就得使用泛型T 来自于基类Number 将< T >改为< reified T: Number>,同时在fun前面加上关键字inline,表示该函数属于内联函数。
//该函数不接收Array<Int>也不接收Array<Double>
fun setArrayNumber(array: Array<Number>) {
var str: String = "数组元素依次排列"
for (item in array) {
str = str + item.toString() + ","
}
}
//只有内联函数才能可以被具体化
inline fun <reified T : Number> setArratStr(array: Array<T>) {
var str: String = "数组元素依次排列"
for (item in array){
str = str + item.toString() + ","
}
}
函数的引用
要把函数作为参数传递给其他函数使用,除了lambda表达式,Kotlin还提供了其他方法,传递函数引用,函数引用可以把一个具名函数转换成一个参值,使用lambda表达式的地方,都可以使用函数引用
fun main() {
showOnBoard("卫生纸",::getDiscountWords)
}
fun getDiscountWords(goodsName:String,hour:Int):String{
val currentYear = 2027
"${currentYear}年,双11${goodsName} 促销倒计时:$hour 小时"
}
fun showOnBoard(goodsName: String, discountWords: (String, Int) -> String) {
val hour = (1..24).shuffled().last()
println(discountWords(goodsName, hour))
}
闭包
Kotlin中能修改并引用定义在自己作用域之外的变量,匿名函数引用着定义自身的函数里的变量,kotlin中的lambda 就是闭包。
能接收函数或者返回函数的函数有叫高级函数,高级函数广泛应用于函数式的编程当中。
【空安全与异常】
在Java 中我们常遇到的空指针异常NullPointerException,给开发者带来很多麻烦,Kotlin作为强大的语言,势必在以往的语言设计经验上进行改良。
null安全
Kotlin区分可空类型和非可空类型,所以你要一个可空变量运行,而它又可能不存在,对于这种危险编译器时刻警惕着。为了应对这种风险,Kotlin不允许你在可空类型值上调用函数,除非你手动接手安全管理。
安全操作符
如果遇到null值,它就跳过函数调用,而不是返回null
fun main() {
val str = readLine()?.capitalize()
println(str)
}
使用带let的安全调用
fun main() {
val str = readLine()?.let {
if (it.isNotBlank()){
it.capitalize()
}else{
"butter"
}
}
println(str)
}
使用非空断言操作符!!
!!. 又称之为感叹号操作符,当变量是null,会抛出NullPointerException
fun main() {
val str = readLine()!!.capitalize()
println(str)
}
使用空合并操作符
?: 操作符的意思呢,就是当左边为null,就使用右面的值
val str = name?:"Butter"
空合并操作符也可以同let函数一起使用,来替代if/else 语句。
fun main() {
var str = readLine()
str = str?.let { it.capitalize() }?:"butter"
println(str)
}
有关异常
先看代码
fun main() {
var number:Int? = null
try {
checkOperation(number)
number!!.plus(1)
}catch (e:Exception){
println(e)
}
}
异常触发的方法
fun checkOperation(number: Int?){
number?:throw UnskilledException()
}
自定义异常
class UnskilledException():IllegalArgumentException("自定义异常 触发")
当出现异常的时候 咱们触发异常的checkOperation 优先触发自定义异常,在现实开发中自定义异常可以做很多事情,比如 像服务端上传异常数据报告等等。
·
匿名函数与安全操作符对后面的开发很重要的,一定要熟记!