Kotlin学习总结——空安全和集合
Posted AC_Jobim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习总结——空安全和集合相关的知识,希望对你有一定的参考价值。
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)
输出结果为:
可以看出,在未做空判断的情况下直接使用操作符!!
的情况下,抛出了空异常
1.6 安全的类型转换——as?操作符
as?
这个操作符表示为安全的类型转换
as
操作符,表示类型转换,如果不能正常转换的情况下使用as?
操作符。当使用as
操作符的使用不能正常的转换的情况下会抛出类型转换(ClassCastException)异常
,而使用as?
操作符则会返回null
,但是不会抛出异常
使用as
例:
// 会抛出ClassCastException异常
val num1 : Int? = "Koltin" as Int
println("nun1 = $num1")
输出结果为:
使用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提供了
toIntOrNull
和toDoubleOrNull
这样的安全转换函数,如果数值不能正确转换,则返回null
var number1 : Int ? = "8.9".toIntOrNull()
println(number1)//null
二、集合相关
Kotlin
中的集合和其他语言不同的是,Kotlin
集合可分为可变和不可变集合。
在
Kotlin
中,集合类型包含三种类型:它们分别是:List
、Set
、Map
,他们之间存在以下几个异同点:
- 它们都是接口,并不是实际的类。
- 它们只实现了
isEmpty()、size、contains()
等函数以及属性。List
和Set
都继承至Collection
接口,且Collection
继承于Iterable
接口。而Map
是独立出来的一个接口。这一点和Java
相同。- 这三种集合类型分别有存在
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空安全 ② ( 手动空安全管理 | 空安全调用操作符 ? | let 函数结合空安全调用操作符使用 )