非空类型与可空类型

在很多编程语言中,如果我们访问了一个空引用,都会收获一个类似于NullPointerException的空指针异常。Kotlin的类型系统区分可空类型和非空类型,来尽力避免空指针异常。

定义一个非空类型和可空类型:

var name: String = "" //定义非空类型name
name = null //非空类型赋值null,编译出错

var name2: String? //定义可空类型
name2 = null  //可以赋值null

对于非空类型,我们可以放心访问它的属性和方法,保证不会出现空指针。

name.length
name.slice(0..2)

对于可空类型,直接访问它的属性和方法有可能收获空指针,而且编译器会直接报错;但我们还是需要访问。一般有两种方式来避免空指针:空检查和使用安全调用符?.

空检查

空检查很好理解,我们在Java中也是这样做的。

name2.length //直接访问,编译报错
if(name2!=null){
    name.length
}

安全调用符

可以这样来访问成员:

name2?.length //如果name2为null,则返回null

可以链式调用:

name2?.substring(3)?.length //只要有一个为null,就返回null
name2?.substring(2) //如果name2为null,则不会执行函数调用

Elvis 操作符

当我们有一个可空类型时,经常会遇到这样的逻辑:如果它不是空,就用它;否则使用另外一个。

if/else写就是这样的:

val l = if(name2!=null) name2.length else -1

用Elvis操作符?:可以简写为:

val l = name2?.length ?: -1

它还可以用在很多这种类似逻辑的场景:

fun findFocusChild(view: View){
    val focusChild = view.getFocusChild() ?: return
    val visibility = focusChild.getVisibility() ?: throw IllegalArgumentException("not visible.")
}

! ! 操作符

!!操作符也叫非空断言运算符。由上面得知,当我们访问一个可空类型的成员或者函数时,可以使用空检查或者安全调用符。但如果你非常确定这个变量一定不为空时,也可以使用!!来进行调用:

var name2: String? = null
//其他赋值逻辑
println(name2!!.length) // 这样也可以避免编译报错

!!操作符的安全性完全由你自己的逻辑保证,编译器不会进行任何的非空判断。这意味着,如果你的逻辑不够严谨,也就是如果name2如果为空,你仍然会收获一个NPE。

而使用安全调用符则可以保证不出现NPE,!!在实际开发中用的很少,除非你能保证它不为空才可以用。

安全的类型转换

接收参数,处理参数,然后输出结果,这是我们软件开发的基本流程。但有时候接收的参数类型并不是很确定,比如我们本来想要对String进行操作,接收到的是Any参数,但我们以为接收的是一个String。代码如下:

var param: Any?  = getParam()
val s = param as String //as是类型转换标识符

如果param真的是一个String,则程序正常工作。但如果它是一个Int呢?又或者它为空呢?这些情况就会导致类型转换失败,收获ClassCastException

我们无法保证接收的参数一切正常,但可以使用as?来进行安全的类型转换:

val s = param as? String 

param不是String或者为空,变量s则为null,而程序并不会出现异常。

更新时间: 4/30/2019, 9:52:46 PM