Kotlin空类型安全(Null Safety)

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>
######本篇文章到此结束,文章若有错误之处,请留言相告,在此先行谢过。万望诸君,多多指教!!!
















    原文作者:顶级工程师闯天涯
    原文地址: https://www.jianshu.com/p/1f0dc4d4ca2b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞