美文网首页
Kotlin学习3

Kotlin学习3

作者: flynnny | 来源:发表于2021-05-18 01:31 被阅读0次

    1kotlin可空性

    提前在编译期强迫重视null问题
    除非另有规定,变量不能为null值。
    为了避免NullPointerException,Kotlin的做法是不让我们给非空类型变量赋null值,但null在Kotlin中依然存在。

    fun main(){
      var str = "asds"
      str = null //报错!!
    
      var str:String ?="asdasd"
      str = null
    }
    

    安全调用操作符

    Kotlin区分可空类型和非可空类型,所以你要一个可空类型变量运行,而他有可能不存在,对于这种潜在危险,编译器时刻警惕着。为了应对这种风险,Kotlin不允许你在可控类型值上调用函数,除非你主动接受安全管理

    选项一:安全调用操作符(?.)
    如果遇到null值,他就跳过函数调用,而不是返回null。

    fun main(){
      val str = readLine()?.capitalize()
      println(str)
    }
    

    使用带let的安全调用

    安全调用允许在可空类型上调用函数,但是如果还想做点额外的事比如创建新值,或判断不为null就调用其他函数,怎么办?可以使用带let函数的安全调用操作符。你可以在任何类型上调用let函数,他的主要作用是让你在指定的作用域内定义一个或多个变量。

    fun main(){
      val str = readLine()?.let{
        if(it.isNotBlank()){
          it.capitalize()
        }else{
          "butterfly"
        }
      }
      println(str)
    }
    

    非空断言操作符(!!.)

    选项二:非空断言操作符
    !!.又称感叹号操作符,当变量是null时,会抛出KotlinNullPointerException。

    fun main(){
      val str = readLine()!!.capitalize()
      println(str)
    }
    

    当变量是null时 ,会去执行函数。

    对比使用if判断null值情况

    选项三:使用if判断null值情况

    fun main(){
      var str = readLine()
      if(str!= null){
        str = str.capitalize()
      }else{
        println("为null。")
      }
    
      //安全操作符支持链式调用
      str = str?.capitalize()?.plus(" is great.")
    
      println(str)
    }
    

    空合并操作符(?:)

    ?:操作符的意思是,如果左边的求值结果为null,就用右边的结果值。

    val str = str ?: "butterfly"
    

    空合并操作符也可以和let函数一起使用来替代if/else语句

    fun main(){
      var str = readLine()
      str = str?.let{it.capitalize()}?:"butterfly"
      println(str)
    }
    

    异常处理与自定义异常

    fun main(){
      car number:Int ?=null
      try{
        checkOperation(number)
        number!!.plus(1)//这里会报异常
      }catch(e:Exception){
        println(e)
      }
    }
    fun checkOperation(number:Int?){
      number?:throw UnskilledException ()
    }
    
    //自定义异常
    class UnskilledException :IlldgalArgumentException("操作不当")
    
    

    先决条件函数

    可以抛出带自定义信息的异常,可以用它定义先决条件,条件必须满足,目标代码才能执行。

    9.png
    fun main(){
      car number:Int ?=null
      try{
        checkOperation(number)
        number!!.plus(1)//这里会报异常
      }catch(e:Exception){
        println(e)
      }
    }
    fun checkOperation(number:Int?){
      checkNotNull(number,{"Something error"})
    }
    

    substring

    字符串截取,substring函数支持IntRange类型(表示一个整数范围的类型)的参数,until创建的范围不包括上限值

    const cal NAME = "Jimmy's friend"
    fun main(){
      val index = NAME.indexOf('\'')
      //NAME.substring(0,index)
      val str= NAME.substring(0 until index)
      println(str)
    }
    

    split

    split函数返回的是List集合数据,List集合又支持解构语法特性,它允许你在一个表达式里给多个变量赋值,解构常用来简化变量的赋值

    const val NAMES = "jack,jacky,jason"
    fun main(){
      val data :List<String>= NAMES.split(',')
      //data[0]
    
      val(origin,dest,proxy) = NAMES.split(',')
    
      println("$origin $dest $proxy")
    }
    

    replace

    fun main(){
      val str1 = "The people's Republic of China."
      //第一个参数是正则表达式,用来决定要替换那些字符
      //第二个参数是匿名函数,用来确定该如何替换正则表达式搜索到的字符
      val str2:String = str1.replace(Regex("[aeiou]")){
        when(it.value){
          "a"->"8"
          "e"->"6"
          "i"->"9"
          "o"->"1"
          "u"->"3"
          else ->it.value
        }
      }
      println(str1)
      println(str2)
    }
    

    ==与===比较

    ==检查两个字符串中的字符是否匹配;
    ===检查两个变量是否指向内存堆上同一个对象。

    fun main(){
      val str1 = "Jason"
      val str2= "jason".capitalize()
    
      println("$str1, $str2")
      println(str1==str2)//true
      println(str1===str2)//false
    }
    

    字符串遍历

    fun main(){
      val str1 = "The people's Republic of China.".forEach{
        print("$it *")
      }
    
    

    数字类型的安全转换函数

    kotlin所有数字类型都是有符号的

    10.png

    kotlin提供了toDoubleOrNull和toIntOrNull这样的安全转换函数,如果树脂不能正确转换,与其触发异常不如干脆返回null值。

    fun main(){
      //抛出异常
      //val number:Int = "8.98".toInt()
      val number:Int ?= "8.98".toIntOrNull()
    
      println(number)
    }
    

    Double转int与类型格式化

    double转int
    精度损失与四舍五入

    fun main(){
      //精度损失
      println(8.956756.toInt())//8
      //四舍五入
      println(8.956756.roundToInt())//9
    
      //格式化 
      val s = "%.2".format(8.956756)
      println(s)//8.96 四舍五入了
    }
    

    标准库函数

    apply

    可以看做一个配置函数,你可以传入一个接收者,然后调用一系列函数来配置它以便使用,如果提供lambda给apply执行,他会返回配置好的接收者。

    fun main(){
      //配置一个file实例
      val file1 = File("E://i have a dream.txt")
      file1.setReadable(true)
      file1.setWritable(true)
      file1.setExrcutable(false)
      //使用apply
      val file2 = File("E://i have a dream.txt").apply{
        setReadable(true)
        setWritable(true)
        setExrcutable(false)
      }
    }
    

    可以看到,调用一个函数类配置接受者时,变量名就省略掉了,这是因为在lambda表达式里,apply能让每个配置函数都作用于接收者,这种行为有时又叫做相关作用域,因为lambda表达式里的所有函数调用都是针对接收者的,或者说,他们是针对接收者的隐式调用

    let

    能使某个变量作用于其lambda表达式里,让it关键字能引用它。let与apply比较,let会把接收者传递给lambda,而apply什么都不传,匿名函数执行完,apply会返回当前接收者,而let会返回lambda的最后一行

    fun main(){
      val res = listOf(3,2,1).first().let{
        it * it
      }
      println(res)//9
    
      println(format("Jack"))
    }
    fun format(guestName:String?):String{
      return guestName?.let{
        "Welcome, $it."
      }?:"what's your name?"
    }
    

    run

    光看作用于来说,run和apply差不多,但run函数不返回接收者,run返回的是lambda结果,也就是true或者false

    import java.io.File
    
    fun main(){
      val file = File("E://i have a dream.txt")
      val result :Boolean = file.run{
        readText().contains("great")
      } 
    
      println(result)//true
    }
    

    run 也可以用来执行函数引用

    fun main (){
      val result = "The people's Republic of China.".run(::isLong)
      println(result)
      //当有多个函数调用,run的优势就显而易见了
      "The people's Republic of China."
            .run(::isLong)
            .run(::showMessage)
            .run(::println)
    }
    
    fun isLong(name :String) = name.length>=10
    
    fun showMessage(isLong:Boolean):String{
      return if(isLong){
         "Name is too long."
      }else{
         "please rename."
      }
    }
    

    with

    with函数是run函数的变体,他们功能行为是一样的,但是with的调用凡事不同,需要值参作为其第一个参数传入

    fun main(){
      val res = "The people's Republic of China.".run{
        length>=10
      }
    
      val isToolong:Boolean = with("The people's Republic of China."){
        length>=10
      }
    }
    

    also

    和let类似,也是把接收者作为值参传给lambda,但有一点不同,also返回接收者对象,而let返回lambda结果。因为这个差异,also尤其适合针对同一原始对象,利用副作用做事,既然also返回的是接收者对象,你就可以基于原始接受者对象执行额外的链式调用

    import java.io.File
    
    fun main(){
      var fileContents:List<String>//没有初始化
      File("E://i have a dream.txt")
            .also{
              println(it.name)
            }.also{
              fileContents = it.readLines()//初始化
            }
      println(fileContents)
    }
    

    takeIf

    takeIf函数需要判断lambda中提供的条件表达式,给出true或false结果。如果是true,从takeIf函数返回接收者对象,如果是false,则返回null。如果需要判断某个条件是否满足,再决定是否可以赋值变量或者执行某项任务,takeIf就非常有用,概念上讲,takeIf函数类似if语句,但他的优势是可以直接在对象实例上调用,避免了临时变量赋值的麻烦。

    import java.io.File
    fun main(){
      val res :String? = File("E://i have a dream.txt")
            .takeIf{
              it.canRead()&&it.canWrite()
            }
            ?.readText()
      println(res)
    }
    

    takeUnless

    takeIf的辅助函数takeUnless,只有判断你给定条件结果是false时,takeUnless才会返回原石接收者对象。

    import java.io.File
    
    fun main(){
      val fileContent3 :String? = File("E://i hava a dream.txt")
            .takeUnless{it.isHidden}
            ?.readText()
      println(fileContent3)
    }
    

    小结回顾

    相关文章

      网友评论

          本文标题:Kotlin学习3

          本文链接:https://www.haomeiwen.com/subject/watsjltx.html