Kotlin学习之路(2) 函数类型与Null安全

接着第一课,第一课只说了如何定义函数,几种类型的参数。接下来要说的就是匿名函数,老规矩先上思维导图。

在这里插入图片描述

【匿名函数与函数类型】

匿名函数
顾名思义定义时不取名字,我们称之为匿名函数,匿名函数通常整体传递给其他函数,或从其他函数返回。
用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 优先触发自定义异常,在现实开发中自定义异常可以做很多事情,比如 像服务端上传异常数据报告等等。

·
匿名函数与安全操作符对后面的开发很重要的,一定要熟记!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值