Kotlin的空安全

2020-11-06  本文已影响0人  132xin

在许多的编程语言(例如Java)中最常见的遇到的陷阱之一,就是访问引用的成员会导致空指针异常的情况(NullPointerException),在Kotlin中的做了空安全的设计,从而避免了NullPointerException。

Kotlin的空安全设计

简单来说就是通过IDE的提示来避免调用null的对象,从而避免了NullPointerException,在androidx里就有通过一个@NonNull的注解就可以标记变量是否为空,然后IDE会帮助检查。到了Kotlin这里就有了语言级别的支持,IDE的提示级别从警告变成了错误,拒绝编译。接下来看看Kotlin的实现空安全做了哪些措施。

变量默认都是不为空的
image.png

可以看到在上面的定义的时候赋值为空的时候,编译器就会提示出现错误,所有的默认变量都是不允许为空的。那么有时间在请求服务器数据的时候,有一些变量是会出现为空的情况,那该怎么办呢?
可以在变量的类型上面加上一个?号,这样就可以解除非空的限制。


image.png

那么这样不是还是会出现空指针异常的情况吗。

安全的调用(?.)

在上面提到通过?号可以解除非空的限制,既然赋值可能会出现为空,那么为了避免空指针的异常,kotlin的做法就是在调用方进行处理——安全的调用。通过?.的方式进行调用。


image.png

上面的?.的意思就是,如果str非空,就返回b.length,否则返回null。如果最后返回为null的话,我们还可以进行一步的处理。

使用Elvis操作符?:

在上面提到的如何str为空,那么length返回为空,这样好像也不太好,这是我们可以使用Elvis操作符:


image.png

意思就是当前面为空的时候就返回-1。Elvis还有另一种常见的用法:


image.png 上面的写法就等价与下面的:可以看到进一步简化了代码。
image.png

!!操作符

!!是非空断言运算符,也是避免空指针异常的手段方式之一,若该值为空则抛出异常。


image.png

意思就编译器不再帮助检查,有什么后果需要程序员去承担,这种方式,实际上和Java就没上两样了。

安全的类型转换

如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException。 另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null:

 val aInt: Int? = a as? Int

可控类型的集合

如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用 filterNotNull 来实现:

val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()

Kotlin的空安全设计,带来了很多好处,但是有的时候我们不一定能在定义的时候赋值,例如在Android开发的时候初始化一个控件的时候,findViewById()是需要在onCreate之后就调用的,那么这种情况怎么处理比较合适呢?

一:利用lateinit关键字进行延迟初始化

延迟初始化的意思是,告诉编译我无法第一时间去初始化,但是在使用前肯定会完成初始化。

    private lateinit var textView:TextView
    override fun onCreate(savedInstanceState: Bundle?) {
      
        textView=findViewById(R.id.textView)
    }

但是延迟初始化并不好,这样跟Java的属性没什么区别了,体验不到了空安全设计到带来的好处,而且 lateinit只能修饰var的属性,那么val的呢?

二:利用by Lazy惰性初始化

lazy()是接受一个 lambda 并返回一个 Lazy <T> 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。也就是说只有第一次调用该属性的时候,委托方法才会被执行。并且只能修饰val的属性。
为了进一步安全使用,上面的TextView的初始化,就可以使用by lazy()了

    private val textView by bindView<TextView>(R.id.textView)
    
    private val Activity.viewFind:Activity.(Int)->View?
        get()={
            findViewById(it)
        }

     fun <V:View> Activity.bindView(id:Int):Lazy<V> = lazy{
        viewFind(id) as V
    }

参考链接:
https://kaixue.io/kotlin-basic-1/

上一篇 下一篇

猜你喜欢

热点阅读