Kotlin学习3

2021-05-18  本文已影响0人  flynnny

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)
}

小结回顾

上一篇下一篇

猜你喜欢

热点阅读