Kotlin学习总结——空安全和集合

Posted AC_Jobim

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习总结——空安全和集合相关的知识,希望对你有一定的参考价值。

参考博客:
Kotlin教程——史上最全面、最详细的学习教程,持续更新中…
Kotlin 基础06 - List、Set、Map、Sequence

一、空安全相关

1.1 定义一个可空类型的变量

定义一个可空类型的变量的格式为:修饰符 变量名 : 类型? = 值,kotlin默认定义的变量不能为空

例:

// 定义一个不可为空的变量,用var修饰的变量可以被重新赋值,用val修饰的变量则不能,但是不能赋值为null
var a : Int = 12
val b : Int = 13

a = 20
// a = null 不能复制为null   
// b = 20   不能被重新赋值

if(a == null){
    // 这样的判断毫无意义,因为变量a永远不可能null
}

/*
    定义可空类型的变量,即变量可以被赋值为null
    定义格式为:修饰符 变量名 : 类型? = 值
*/
var nullA : Int? = 12
val nullB : Int? = 13

nullA = null

if(nullA == null){
    println("nullA = $nullA")
}

注意:要定义一个可空类型的变量时,即在定义变量的类型后面加上?符号就行了。在使用的时候,记住要判断该段该变量是否为空。

1.2 安全调用操作符——?.

判断可空类型的两种使用方式:

  • if…else…判断
  • 符号?.判断

使用符号?.判断

  • 该符号的用法为:可空类型变量?.属性/方法。当对象不为空时正常调用相应的方法,对象为空就返回null
  • 这种用法大量用于链式操作的用法中,能有效避免空引用异常(NullPointException),因为只要链式其中的一个为null,则整个表达式都为null

例:

var str : String? = "123456"
str = null
// 当变量str为null时,会返回空(null)
println(str?.length)   //null

if...else...判断

例:

var str : String? = "123456"
str = null

if (str == null){
    println("变量str为空")//变量str为空
}else{
    println("str.length => ${str.length}")
}

链式调用

?.这种符号去判断是否为null,在Kotlin中使用的地方是很多,特别是对于链式调用来说体验性更好。

例:这里简单写一个建造者模式,来模拟?.在链式调用中的用法

class Test{

    class Builder{
        private var name : String? = "Tom"
        private var age : Int? = 0
        private var sex : String? = "男"

        fun setName(name : String) : Builder?{
            this.name = name
            return this
        }

        fun setAge(age : Int) : Builder?{
            this.age = age
            return this
        }

        fun setSex(sex: String?) : Builder?{
            this.sex = sex
            return this
        }

        override fun toString(): String {
            return "Builder(name=$name, age=$age, sex=$sex)"
        }
    }
}

fun main(args: Array<String>) {
    val builder : Test.Builder? = Test.Builder().setName("Lily")?.setSex("nv")?.setAge(10)
    println(builder.toString())
}

输出结果为:

Builder(name=Lily, age=10, sex=女)

函数中使用可空类型时

当一个函数/方法有返回值时,如果方法中的代码使用?.去返回一个值,那么方法的返回值的类型后面也要加上?符号

例:

fun funNullMethod() : Int? {
    val str : String? = "123456"
    return str?.length
}

输出结果为:

6

1.3 带let的安全调用

let的用法:变量?.let{ ... }
当变量不为空的时候执行let函数,而且会将被调用对象本身作为参数传递到lambda表达式中。

例:排除掉数组中的空元素

val arrTest : Array<Int?> = arrayOf(1,2,null,3,null,5,6,null)

// 传统写法
for (index in arrTest) {
    if (index == null){
        continue
    }
    println("index => $index")
}

// let写法
for (index in arrTest) {
    index?.let { println("index => $it") }
}

输出结果为:

index => 1
index => 2
index => 3
index => 5
index => 6

1.4 ?:操作符

?: 这个操作符的左右两边都接收一个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

例:

val testStr : String? = null

var length = 0

// 例: 当testStr不为空时,输出其长度,反之输出-1

// 传统写法
length = if (testStr != null) testStr.length else -1

// ?: 写法
length = testStr?.length ?: -1

println(length)

输出结果为:

-1

分析:此操作符一般和?.操作符连用。当且仅当?:左边的表达式为null时,才会执行?:右边的表达式。

1.5 非空断言操作符——!!操作符

!! 这个操作符表示在判断一个可空类型时,会显示的抛出空引用异常(NullPointException)

例:

val testStr : String? = null
println(testStr!!.length)

输出结果为:
img

可以看出,在未做空判断的情况下直接使用操作符!!的情况下,抛出了空异常

1.6 安全的类型转换——as?操作符

as? 这个操作符表示为安全的类型转换

as操作符,表示类型转换,如果不能正常转换的情况下使用as?操作符。当使用as操作符的使用不能正常的转换的情况下会抛出类型转换(ClassCastException)异常,而使用as?操作符则会返回null,但是不会抛出异常

使用as

例:

// 会抛出ClassCastException异常
val num1 : Int? = "Koltin" as Int
println("nun1 = $num1")

输出结果为:
img

使用as?

例:

val num2 : Int? = "Koltin" as? Int
println("nun2 = $num2)

输出结果为:

num2 = null

1.7 类型检测及自动类型转换——is运算符

我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // 做过类型判断以后,obj会被系统自动转换为String类型
    return obj.length 
  }

  // 这里的obj仍然是Any类型的引用
  return null
}

或者

fun getStringLength(obj: Any): Int? {
  if (obj !is String)
    return null
  // 在这个分支中, `obj` 的类型会被自动转换为 `String`
  return obj.length
}

1.8 安全转换函数

Kotlin提供了toIntOrNulltoDoubleOrNull这样的安全转换函数,如果数值不能正确转换,则返回null

var number1 : Int ? = "8.9".toIntOrNull()
println(number1)//null

二、集合相关

Kotlin中的集合和其他语言不同的是,Kotlin集合可分为可变和不可变集合。

Kotlin中,集合类型包含三种类型:它们分别是:ListSetMap,他们之间存在以下几个异同点:

  1. 它们都是接口,并不是实际的类。
  2. 它们只实现了isEmpty()、size、contains()等函数以及属性。
  3. ListSet都继承至Collection接口,且Collection继承于Iterable接口。而Map是独立出来的一个接口。这一点和Java相同。
  4. 这三种集合类型分别有存在MutableList、MutableSet、MutableMap接口,这些接口中提供了改变、操作集合的方法。例如add()clear()remove()等函数。

由以上几点我们可出,在定义集合类型变量的时候如果使用List<E>Set<E>Map<K,V>声明的时候该集合则是不可变集合,而使用MutableList<E>MutableSet<E>MutableMap<K,V>的时候该集合才是可变类型集合。

2.1 List类型

一个接口是不能直接实例化的,那我们要初始化一个怎么做呢?其实Kotlin提供了相应的标准库函数去初始化。

初始化:

  • 声明并初始化List的集合:使用listOf(..)函数
  • 声明并初始化MutableList的集合:使用mutableListOf(..)函数

常用方法:
在这里插入图片描述
元素的安全获取:
在这里插入图片描述
集合遍历:
在这里插入图片描述
List支持使用toList和toMutableList函数动态实现只读列表和可变列表的相互转换

代码示例:

fun main() {
    //不可变列表
    val list = listOf("Jason", "Jack", "Jacky")
//    println(list[3])//java.lang.ArrayIndexOutOfBoundsException

    //安全索引取值函数
    println(list.getOrElse(3){"Unknown"})//Unknown
    println(list.getOrNull(3))//null,安全索引取值函数,如果没有,返回null结果
    println(list.getOrNull(3)?:"Unknown")//Unknown

    //可变列表
    val mutableList = mutableListOf("Jason", "Jack", "Jacky")
    mutableList.add(0,"zb")
    mutableList.add(0,"zb")
    println(mutableList)//[zb, zb, Jason, Jack, Jacky]
    mutableList.remove("Jack")
    println(mutableList)//[zb, zb, Jason, Jacky]

    //两者之间的相互转换
    list.toMutableList()
    mutableList.toList()

    mutableList += "Jimmy"
    println(mutableList)//[zb, zb, Jason, Jacky, Jimmy]
    mutableList.removeIf{it.contains("J")}//移除所有包含J的元素
    println(mutableList)//[zb, zb]

    //集合的遍历
    for(s in list){
        println(s)
    }

    list.forEach{println(it)}

    //遍历时获取索引
    list.forEachIndexed{index,item->
        println("$index $item")
    }
    
}

2.2 Set类型

  • Set 集合里的所有元素都具有唯一性。
  • Set 集合不支持基于索引的存取值函数,因为它里面的元素顺序不固定。可以通过elementAt函数读取集合中的元素

初始化:

  • 声明并初始化Set的集合:使用setOf(..)函数
  • 声明并初始化MutableSet的集合:使用mutableSetOf(..)函数

常用方法:在这里插入图片描述
List与Set的转换
在这里插入图片描述

代码示例:

fun main() {

    val set = setOf("Kotlin", "Java", "Scala")
    //通过elementAt函数读取集合中的元素
    println(set.elementAt(2))

    //可变集合
    val mutableSet = mutableSetOf("Kotlin", "Java", "Scala")
    mutableSet += "Groovy"

    //集合转换,把list转换成set,去掉重复元素
    val list = listOf("Kotlin", "Java", "Scala","Scala").toSet().toList()

    //去除重复元素
    listOf("Kotlin", "Java", "Scala","Scala").distinct()
}

2.3 Map类型

  • 不可变的Map类型集合的初始化使用:mapOf()函数
  • 可变的Map类型集合的初始化使用:mutableMapOf()函数

读取Map的值:
在这里插入图片描述
常用方法:
在这里插入图片描述

代码示例:

fun main() {
	//map的创建
    val map = mapOf("Jack" to 20, "Json" to 18)
    println(map)//{Jack=20, Json=18}

    mapOf(Pair("Jack",20),Pair("Jason",18))

    //读取map的值
    println(map[0])
    println(map.getValue("Jack"))
    println(map.getOrElse("Json1"){"Unknown"})
    println(map.getOrDefault("Rose",0))

    //map的遍历
    map.forEach{
        println("$it.key ${it.value}")
    }

    map.forEach{(key,value)->
        println("$key $value")
    }

    //可变map
    val mutableMap = mutableMapOf("Jack" to 20,"Jason" to 18)
    mutableMapOf(Pair("Jack", 20), Pair("Jason", 18))

    mutableMap += "Jimmy" to 30
    println(mutableMap)//{Jack=20, Jason=18, Jimmy=30}
    mutableMap.getOrPut("Rose"){18}
    println(mutableMap)//{Jack=20, Jason=18, Jimmy=30, Rose=18}

}

以上是关于Kotlin学习总结——空安全和集合的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin空安全总结 ( 变量可空性 | 手动空安全管理 | 空安全调用操作符 | 非空断言操作符 | 空合并操作符 | 空指针异常处理 | 先决条件函数判空 )

Kotlin小笔记kotlin中的空指针检查

Kotlin空安全 ② ( 手动空安全管理 | 空安全调用操作符 ? | let 函数结合空安全调用操作符使用 )

Kotlin学习总结——变量常量数据类型条件语句

使用 Kotlin 在片段中引用 RecyclerView 时出现空指针错误

使用 Kotlin 更改片段中的按钮背景