Kotlin——空类型安全(Null Safety)
本篇文章主要是翻译自Kotlin官方文档的Null Safety章节,同时加入自己的一些理解和思考。
可空类型(Nullable types)和非空类型(Non-Null Types)
Kotlin语言的类型系统旨在从代码层面来消除空引用的危险。空引用亦被称为Billion Dollar Mistake(十亿美金错误)。
在包括Java在内很多编程语言中,一个最常见的缺陷就是:使用一个空引用成员,导致空引用异常。在Java语言中,这个空引用异常就等价于NullPointerException(简写为NPE);
如上所述,Kotlin语言的类型系统就是为了从代码层面来消除空指针异常(NullPointException)的。唯一可能引起空指针异常(NPE)的原因是:
1. 一个明确的调用去抛出空指针异常(NullPointerException);2. 操作符 !! 的使用;
3. 调用外部的Java代码引起的;
4. 一些关于数据初始化的不一致(构造方法中的未初始化的变量在其它地方被使用);
在Kotlin语言中,类型系统可以识别出引用是否可以设置为null(nullable references),或者不可以为null (non-null references)。举个例子,一个常规的String类型变量是不可以设置为 null的。
var a :String = "abc"
a = null //Error: Null can not be a value of a non-null type String
为了允许为null,我们可以将变量声明为可空String类型,写作:String?
var b:String? = "abc"
b = null // that's OK
切记,对于Kotlin的其它类型而言,也是只需要在类型后面加上?,即可表示为该类型可空。(尽量不使用,除非必须)
现在我们看一下空引用的属性和方法,很显然,这是不安全,编译器会报错:
var nullObj:String? = null
length = nullObj.length // error: 因为 nullObj 是一个null
但是我们需要去访问这些属性,这里有几种方法可以帮助我们。
一:条件检查
我们明确地来判断变量是否为null,然后针对两种情况进行独立的处理。
值得注意的是,这种方法仅仅适用于变量不可变的情况(也就是说:一个本地变量在检查和使用过程中没有被修改,或者是不可变的变量val类型的),因为这种情况可能在检查之后被修改为null.
//第一种方式
val len = if( b != null) b.length else 0
//复杂一点的方式
if(b != null && b.length >0){
println(b.length)
}else{
println("Empty String")
}
二:安全调用(?.)
我们可以通过使用安全调用操作符(?.)来实现。
// var len = b.length
var len = b ?. length
当b 不为 null时,就返回b.length,否则返回null
安全调用在链式调用方面非常有用。举个例子:如果今天是晴天,那么我就去钓鱼,如果钓到鱼,那么我就吃鱼;
today?.sunshine?.goFishing?.eatFish()
这样的链式调用,如果有任何一个属性为null,则返回 null;
##let关键字
执行特定的操作只获取非空的值,我们可以使用安全调用操作符和let关键字:
// 建立一个Car 数据类
data class Car(val brand:String,val color:String? = null)
// 建立一个集合
val cars = listOf(
Car("Audi"),
Car("BMW","White"),
Car("Buck","Golden"),
Car("Jeep","Gray"),
Car("Benz","Black")
)
for(car in cars){
car.color?.let{ println(car) } // ignore Car("Audi")
}
##三:Elvis 操作符(**?:**)
当我们在使用可为空的引用reference时,我们可以通过**if**语句来判断它在不为nul情况下使用它,否则使用一些非空的值.比如,我们要返回String?的变量的长度
//一般做法
val strLen:Int = if(reference != null) reference.length else 0
//使用 Elvis操作符(?:)
val strLength = reference?.length ?: 0
如果**?:**左边的表达式不为 null 时,则 elvis操作符返回该表达式的值,否则返回 操作符**?:**右边的值。
  **值得注意的是,只有在操作符右边的表达式为null的情况下,才会评估右边的表达式的值。**
  还有一点要注意的是:虽然**throw **和** return ** 在Kotlin语言中都是表达式,但是它们也可以用在elvis操作符的右边。这点是非常便利的。
val color = car.getColor() ?: return null
val brand = car.getBrand() ?: throw IllegalArgumentException(“Brand can not be null)
其实我们会发现,Elvis操作符**类似于**java中的三元运算符,例如我们在RecyclerView异或ListView中的Adapter的getItemCount()方法中所写的:
return (list != null) ? list.size : 0;
##四:操作符(!!)
这个操作符只返回非空类型的值,如果变量为空,则直接抛出空指针异常。因此,如果你希望得到NPE,你可以使用它,但是你不得不明确地调用它,这样它才不会突然出现。
val len = b!! . length
##五:安全转型(as?)
首先说一下原文中的**cast**在字典里的有一个意思是“**造型**”,故这里翻译为转型。
常规的类型转换在对象不是目标类型式,会导致ClassCastException异常。当我们使用 **as?**操作时,如果转型不成功则返回null.避免了发生转型异常。
val aInt :Int? = 1.0f as? Int
##六:可空类型的集合
如果我们的集合中有可空类型的元素,且我们先过滤掉那些为空的元素,则可以使用方法——**filterNotNull()**
val nullableList : List<String?> = listOf(
“red”,
“orange”,
“yellow”,
“gree”,
“blue”,
null
)
//过滤掉null
val colors:List<String> = nullableList.filterNotNull()
<br>
<br>
######本篇文章到此结束,文章若有错误之处,请留言相告,在此先行谢过。万望诸君,多多指教!!!