美文网首页
Kotlin学习4

Kotlin学习4

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

    集合

    List、set 、map 分两类,只读和可变

    List创建与元素获取

    getOrElse是一个安全索引取值函数,他需要两个参数,第一个是索引值,第二个是能提供默认值的lambda表达式,如果索引值不存在的话,可用来代替异常。

    getOrNull是Kotlin提供的另一个安全索引取值函数,他返回null结果,而不是抛出异常。

    fun main(){
      val list :List<String> = listOf("jason","jack","jacky")
      //list[4]
      println(list.getOrElse(4){"Unknown"})//Unknown
      println(list.getOrNull(4))//null
      println(list.getOrNull(4)?:"Unknow")
    }
    

    可变List集合

    在Kotlin中,支持内容修改的列表叫可变列表,要创建可变列表,可以使用mutableListOf函数。list还支持使用toList和toMutableList函数动态实现只读列表和可变列表的相互转换。

    fun main(){
      val mutableList = mutableListOf("jason","jack","jacky")
      mutableList.add("jimmy")
      mutableList.remove("jack")
      println(mutableList)
      
      listOf("jason","jack","jacky").toMutableList()
      mutableListOf("jason","jack","jacky").toList()
    }
    

    mutator函数

    能够修改可变列表的函数有个统一的名字:mutator函数
    添加元素运算符与删除元素运算符
    基于lambda表达式指定的条件删除元素

    fun main(){
      val mutableList =mutableListOf("jason","jack","jacky")
      mutableList += "jimmy"
      println(mutableList)
    
      mutableList -="jason"
      println(mutableList)
      
    //removeIf
      mutableList.removeIf{it.contains("jack")}
      println(mutableList)
    }
    

    List集合遍历

    for in 遍历
    forEach 遍历
    forEachIndexed遍历时要获取索引

    fun mian(){
      val list = listOf("jason","jack","jacky")
      for(s :String in list){
        println(s)
      }
      list.forEach{
        println(it)
      }
      list.forEachIndexed {index,item->
        println("$index, $item")
      }
    }
    

    解构语法过滤元素

    通过_符号过滤不要的元素

    fun main(){
      val list = listOf("jason","jack","jacky")
      val (origin,_,proxy) = list
    }
    

    set集合

    set创建与元素获取

    list可以有重复的;set不可以
    通过setOf创建set集合,使用elementAt函数读取集合中的元素

    fun main(){
      val set = setOf("Kotlin","Java","Scala")
      //set[3]//  没有这种写法
      set.elementAt(2)
    }
    

    可变set集合

    通过mutableSetOf创建可变的set集合

    fun main(){
      val mutableSet = mutableSetOf("Kotlin","Java","Scala")
      mutableSet +="Groovy"
    }
    

    集合转换与快捷函数

    把list转换成set,去掉重复元素
    快捷函数

    fun main(){
      val list = listOf("jason","jack","jacky","jacky" )
            .toSet()
            .toList()
      println(list)
       
      println(listOf("jason","jack","jacky","jacky").distinct())
    }
    

    数组类型

    Kotlin提供各种Array,虽然是引用类型,但是可以编译成java基本数据类型

    11.png
    fun main(){
      val intArray = intArrayOf(10,20,30)
      listOf(10,20,30).toIntArray()
      
    }
    

    map集合

    map的创建

    to看上去像关键字,实际上,他是个省略了点号和参数的特殊函数,to函数将它左边和右边的值转化成一对Pair

    fun main(){
      val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
      println(map)
      
      mapOf(Pair("Jack",20),Pair("Jason",18))
    }
    

    获取map的值

    []取值运算符,读取键对应的值,如果键不存在就返回null
    getValue,读取键对应的值,如果键不存在就抛出异常
    getOrElse,读取键对应的值,或者使用匿名函数返回默认值
    getOrDefault,读取键对应的值,或者返回默认值

    fun main(){
      val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
      println(map["Jack"])//30
      println(map.getValue("Jack"))//30
      println(map.getOrElse("Rose"){"unknow"})//unknow
      println(map.getOrDefault("Rose"),0)//0
    }
    

    遍历map

    forEach

    fun main(){
      val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
      map.forEach{ it :Map.Entry<String,Int>
        println("${it.key} , ${it.value}")
      }
      map.forEach{ (key:String, value:Int) ->
        println("${key} , ${value}")
      }
    }
    

    可变map集合

    通过mutableMapOf创建可变map
    getOrPut键值不存在,就添加并返回结果,否则就返回已有键对应的值

    fun main(){
      val mutableMap = mutableMapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
      mutableMap +="Jimmy" to 30
      mutableMap.put("Jimmy",31)//覆盖了
    
      println(mutableMap.getOrPut("Jimmy"){18})
      mutableMap.getOrPut("Rose"){18}
      println(mutableMap )
    }
    

    定义类与field关键字

    针对你定义的每一个属性,kotlin都会产生一个field,一个get,一个setter,field用来存储属性数据,你不能直接定义field,kotlin会封装field,保护她里面的数据,质保路给getter和setter使用。属性的getter方法决定你如何读取属性值,每个属性都有getter方法,setter方法决定你如何给属性赋值,所以只有可变属性才会有setter方法,尽管kotllin会自动提供默认的getter和setter方法,但在需要控制如何读写属性数据时,你也可以自定义他们。

    class player{
      var name = "jack"
            get() = field.capitalize()
            set(value){
              field = value.trim()
            }
    }
    

    计算属性与防范竞态条件

    计算属性是通过一个覆盖的get或set运算符来定义的,这时field就不需要了

    class Player{
      val rolledValue
            get()=(1..6).shuffled().first()
     }
    

    如果一个类的属性即可空又可变,那么引用他之前你必须保证他非空,一个办法是用also标准函数

    class Player{
      var words:String ?="hello"
    
      fun say(){
        words?.also{it:String
          println("hello ${it.toUpperCase()}")
        }
      }
    }
    

    主构造函数

    我们在Player类的定义头中定义一个柱构造函数,使用临时变量为player的各个属性提供初始值,在kotlin中,为便于识别,临时变量(包括仅仅引用一次的参数),通常都会以下划线开头的名字命名。

    class Player(
      _name:String,
      _age:Int,
      _isNormal:Boolean
    ){
      var name = _name
            get() = field.capotalize()
            set(value){
              field = value.trim()
            }
      var age = _age
      var isNormal = _isNormal
    }
    fun main(){
      var player = Player("jack",20,true)
    }
    

    在柱构造函数里定义属性

    kotlin允许你不使用临时变量赋值,而是直接用一个定义同时指定参数和类属性,通常,我们更喜欢用这种方式定义类属性,因为能减少重复代码

    class Player2(
      _name:String,
      var age:Int,
      val isNormal:Boolean
    ){
      var name = _name
            get() = field.capotalize()
            set(value){
              field = value.trim()
            }
    }
    

    次构造函数

    可以定义多个次构造函数来配置不同参数组合

    class Player2(
      _name:String,
      var age:Int,
      val isNormal:Boolean
    ){
      var name = _name
            get() = field.capotalize()
            set(value){
              field = value.trim()
            }
    
      constructor(_name:String):this(_name,
                                              age=100
                                              isNormal = false){
        //...
      }
    }
    

    默认参数

    定义构造函数时可以指定默认值。

    class Player3(
      _name:String,
      var age:Int = 20,
      val isNormal:Boolean
    ){
      var name = _name
            get() = field.capotalize()
            set(value){
              field = value.trim()
            }
    }
    

    初始化块

    初始化块可以设置变量或值,以及执行有效性检查,如检查传给某构造函数的值是否有效,初始化块代码会在构造类实例时执行。

    init{
      require(age>0){"age must be positive"}
      require(name.isNotBlank()){"must have a name"}
    }
    
    

    初始化顺序

    主构造函数里声明的属性
    类级别的属性赋值
    init初始化块里的属性赋值和函数调用
    次构造函数里的属性赋值和函数调用

    class Student(
      _name:String,
      val age:Int
    ){
      var name = _name
      var score = 10
      private val hobby ="music"
      val subject :String
    
      init {
        println("initializing student")
        subject = "math"
      }
    
      constructor(_name :String):this(_name,10){
        score = 20
      }
    }
    fun main(){
      Student("Jack")
    }
    
    12.png

    延迟初始化lateinit

    使用lateinit关键字相当于做了一个约定:在用它之前负责初始化
    只要无法确认lateinit变量是否完成初始化,可以执行inInitialized检查

    class Player4{
      laterinit var equipment:String
      fun ready(){
        equipment = "sharp knife"
      }
      fun battle(){
        if(::equipment.isInitialized)println(equipment)//没有初始化就不执行了
      }
    }
    fun main(){
      val p = Player4()
      p.ready()//如果忘了就异常了
      p.battle()
    }
    

    惰性初始化by lazy

    暂时不初始化某个变量,直到首次使用它

    class Player5(_name :String){
      var name = _name
      val config by lazy{loadConfig()}
      private fun loadConfig():String{
        println("loading...")
        return "xxx"
      } 
    }
    fun main(){
      val p = Player5("Jack")
      Thread.sleep(3000)
      println(p.config)
    }
    

    初始化陷阱1

    在初始化块时,顺序非常重要,必须保证块中所有属性已完成初始化。

    class Player6(){
      init{
        val bloodBonus = blood.times(4)//blood 报错
      }
      val blood = 100
    }
    

    虽然 类级别属性 比初始化块的 先初始化,但还是要放在前面。顺序不能反了,从上到下。

    class Player6(){
      val blood = 100//要在前面
      init{
        val bloodBonus = blood.times(4)
      }
    }
    

    初始化陷阱2

    下面这个便已没有问题,因为编译器看到name已经在init里初始化了,但是代码一运行,就会抛出空指针异常,因为name属性还没赋值,firstLetter函数就用了他。

    class Player7(){
      val name :String
      private fun firstLetter() = name[0]
      
      init {
        println(firstLetter())
        name = "Jack"//和上面一行换一下就可以了
      }
    }
    

    初始化陷阱3

    因为编译器看到所有属性都初始化了,所以编译没有问题,但运行确是null。
    因为在用initPlayerName函数初始化playerName时,name属性还没有完成初始化

    class Player8(_name :String){
      val playerName:String = initPlayerName()//这时拿到的是null
      val name:String = _name//把这行放到第一行即可
      private fun initPlayerName() = name
    }
    fun main(){
      println(Player8("Jack").playerName)
    }
    

    小结

    相关文章

      网友评论

          本文标题:Kotlin学习4

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