Kotlin学习4

集合

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

List创建与元素获取

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

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

fun main(){
  val list :List = 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
    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)