kotlin的可空性,空安全

一,可空性

可空性是 Kotlin 类型系统中帮助你避免 NullPointerException 错误的特性。 作为一个程序的用户,你很可能见过像这样干巴巴的错误信息“An error has occurred: java.lang.NullPointerException”(发生了错误 : java.lang.NullPointerException)。 这条信息还有另外一个版本“Unfortunately, the application X has stopped”(对不起, X 应 用程序己停止),其背后的隐藏原因往往也是 NullPointerException。这样的 错误让用户和开发者都觉得庆烦。

现代编程语言包括 Koti in 解决这类问题的方法是把运行时的错误转变成编译期 的错误。通过支持作为类型系统的一部分的可空性,编译器就能在编译期发现很多 潜在的错误,从而减少运行时抛出异常的可能性。

二,可空类型

Kotlin 和 Java 的类型系统之间第一条也可能是最重要的一条区别是, Kotlin 对可空类型的显式的支持。这意味着什么呢?这是一种指出你的程序中哪些变量和属 性允许为 null 的方式。如果一个变量可以为 null,对变量的方法的调用就是不安全的,因为这样会导致 NullPointerException。 Kotlin 不允许这样的调用, 因而可以阻止许多可能的异常

fun strLen(s: String) = s.length

strLen(null)

如上面的代码和调用是会报Error的。
ERROR: Null can not be a value of a non-null type String

在kotlin中我们用“问号 ?”来表示可空
type? = type or null
问号加在任何类型的后面表示这个类型的变量可以存储Null引用。
重申一下,没有问号的类型表示这种类型的变量不能存储null引用,这说明所有常见类型默认都是非空的。除非显式的把它标记为可空。

还是上面的代码,如果加上“问号?” 支持可空类型

fun strLen(s: String?) = s.length

因为s支持null的引用,这个时候s.length是会报异常的。所以这行代码会有Error提示
ERROR: only safe (?) or non null asserted (! !.) calls are allowed on a nullable receiver of type kotlin.String?

所以正确的写法应该是

fun strLenSafa(s: String?): Int = if(s != null) s.length else 0

增加了null引用的判断就不会有问题了。

三,可空安全调用的几种运算符

安全调用运算符”?.”

可以说是Kotlin中最有效的一种安全调用运算符,它允许你把一次null检查和一次方法调用合并成一个操作
s?.toUpperCase() 等同于下面的写法:

if(s != null) s.toUpperCase() esle null

换句话说如果你试图调用一个非空的方法,这次方法调用会被正常执行,但是如果值是null,这次调用不会发生,二整个表达式的值为null

fun strLen1(s: String?) = s?.length

类似于这种写法,当s不为空的时候 返回的是一个int长度,但是当为空的时候并不会返回一个0,而是null

class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person1(val name: String, val company: Company?)

fun printShippingLabel(person: Person1) {
    val address = person.company?.address ?: throw IllegalArgumentException("No Address")
    with(address) {
        println(streetAddress)
        println("$zipCode  $city,  $country")
    }
}

带null检查的方法调用序列在java中太常见了。类似于上面的这种,kotlin可以让他们变得更加简洁。

Elvis运算符“?:”

kotlin有更方便的运算符来提供null的默认值

fun foo(s: String?) {
    val t: String = s ?: ""
    println("zifu:" + t)
}

如果s为null,结果就是一个空字符串

Elvis 运算符接收两个运算数,如果第一个运算数不为 null,运算结果就是第 一个运算数;如果第一个运算数为 null,运算结果就是第二个运算数。

fun strLengthSafe(s: String): Int = s?.length ?: 0

安全转换“as?”

as 运算符。和 常规的 Java 类型转换一样,如果被转换的值不是你试图转换的类型,就会抛出 ClassCastException 异常。当然可以结合 is 检查来确保这个值拥有合适的类型。 但是作为一种安全简洁的语言, Kotlin 没有更优雅的解决方案吗?当然有。

as ?运算符尝试把值转换成指定的类型, 如果值不是合适的类型就返回 null,
一种常见的模式是把安全转换和 Elvis 运算符结合使用。例如,实现 equals 方法的时候这样的用法非常方便。

class Person(val firstName: String,val lastName: String){
    override fun equals(o: Any?): Boolean{
        val otherPerson = o as? Person ?: return false
        return otherPerson.firstName == firstName && otherPerson.lastName == lastName
    }

    override fun hashCode(): Int = firstName.hashCode() * 37 + lastName.hashCode
}

使用这种模式,可以非常容易地检查实参是否是适当的类型,转换它,并在它 的类型不正确时返回 false,而且这些操作全部在同一个表达式中。当然,这种场 景下智能转换也会生效 : 当你检查过类型并拒绝了 null 值,编译器就确定了变量 otherPerson 值的类型是 Person 并让你能够相应地使用它。

非空断言“!!”

非空断言是 Kotlin 提供给你的最简单直率的处理可空类型值的工具。它使用双感叹号表示,可以把任何值转换成非空类型。如果对 null 值做非空断言,则会抛出异常
如前面的代码

fun strLen(s: String?) = s.length
fun strLen(s: String?) = s!!.length

第一行代码会报Error,但是第二行就不会了。可以顺利编译通过。
如果第二行中的s为null会发生什么?kotlin没有其他选择,只会抛出空指针异常。

“let”函数

let 函数让处理可空表达式变得更容易。 和安全调用运算符一起,它允许你对表达式求值,检查求值结果是否为 null,并把结果保存为一个变量。 所有这些动作都在同一个简洁的表达式中。

fun sendEmailTo(email: String) {
    println("Sending email to $email")
}

var email: String? = "[email protected]"
    email?.let { sendEmailTo(it) }
    email = null
    email?.let { sendEmailTo(it) }

如果email = null则下面的一行代码不会被调用

可空类型的扩展

为可空类型定义扩展函数是一种更强大的处理 null 值的方式。可以允许接收 者为 null 的(扩展函数)调用,并在该函数中处理 null,而不是在确保变量不 为 null 之后再调用它的方法。只有扩展函数才能做到这一点,普通成员方法的调 用是通过对象实例来分发的,因此实例为 null 时(成员方法)永远不能被执行。

Kotlin 标准库中定义的 String 的两个扩展函数 isEmpty 和 isBlank 就 是这样的例子。第一个函数判断字符串是否是一个空的字符串 ””。第二个函数 则判断它是否是空的或者它只包含空白字符。通常用这些函数来检查字符串是有 价值的,以确保对它的操作是有意义的。你可能意识到了,像处理无意义的空字 符串和空白字符串这样处理 null 也很有用。事实上,你的确可以这样做:函数 isEmptyOrNull 和 isNullOrBlank 就可以由 String?类型的接收者调用。

fun verifyUserInput(input: String){
    if(input.isNullOrBlank()){
        println("please fill in the required fields")
    }
}

函数 isNullOrBlank 显式地检查了 null ,这种情况下返回 true ,然后调 用 isBlank,它只能在非空 String 上调用:

fun String?.isNullOrBlank(): Boolean = this == null || this.isBlank()

以上,描述了kotlin关于可空,和空处理的几种方式。

参考:《实战kotlin》

示例代码:https://github.com/wangxp423/ExerciseKt

kotlin之Android项目实战

猜你喜欢

转载自blog.csdn.net/wangxp423/article/details/80683228