Kotlin编程语言使用问号(?)符号来表示其许多空值安全功能。
空值安全是现代编程语言的一个特点,它是为了减少(或消除)源代码中引用空值的危险。
为了理解Kotlin的空值安全功能是如何工作的,让我们先看看Java中的一个例子。
下面的代码在Java中是有效的:
class Example {
public static void main(String[] args) {
String myString = null;
myString.length();
}
}
但是当你运行上面的代码时,对myString.length() 的调用会产生一个错误,如下图所示:
Exception in thread "main" java.lang.NullPointerException
at Example.main(example.java:4)
NullPointerException 的发生是因为上面的myString 变量的值是null ,而这个null 的值并不能访问.length() 方法。
Kotlin的设计是为了防止NullPointerException 错误,这就是在该语言中加入null安全特性的原因。
在Kotlin中,你不能将null 赋值给一个类型为String 的变量,如下图所示:
var myString: String = null
// Error: null can not be a value of non-null String type
要想在你的变量中允许null ,你需要在变量类型后面添加一个问号? :
var myString: String? = null
然后,该变量就变成了一个可空的String 类型,它可以容纳null 的值。
String 和String? 之间的这种不同类型也允许Kotlin编译器在你运行源代码之前报告任何与null 有关的错误。
例如,假设你想检查String? 类型中的length 。默认情况下,Kotlin会抱怨说你需要执行一个安全调用:
var myString: String? = null
myString.length // Error: only safe or asserted call allowed!
对于一个nullable类型的安全调用是通过在属性或方法调用前添加一个问号来完成的。
考虑一下下面的例子:
var myString: String? = null
myString?.length // ok
myString?.uppercase() // ok
上面代码中的属性和方法调用都是安全调用。当变量的值是null ,那么Kotlin只是返回null ,而不执行属性或方法调用。
对属性和方法的非空断言调用
除了上面的安全调用,Kotlin还有非空的断言调用,它使用了双bang操作符 (!!)
非空值断言调用是在你绝对确定nullable类型不是null ,所以你不需要安全调用时使用的。
考虑一下下面的例子:
var myString: String? = null
myString = "abc"
myString!!.length
myString!!.uppercase()
你可以看到,在调用.length 和.uppercase() 时,myString 的值已经不是null 。
但是Kotlin仍然抱怨说你需要进行安全调用或者断言调用,因为String? 类型为nullable。
因为我们确定myString 的值是abc ,所以你可以使用!! 操作符做一个非空的断言式调用。
当你对一个有null 值的变量进行非空的断言调用时,Kotlin会抛出NullPointerException 错误:
var myString: String? = null
myString!!.length // Error: NullPointerException
正如你所看到的,Kotlin中的问号是一个伟大的功能,它可以帮助你避免由null 值引起的错误。
安全转换的问号
问号也可以在使用as 关键字来铸造一个变量时使用:
var myVar: Any = "String"
var myInt: Int? = myVar as? Int // returns null
当铸造不可能时,那么Kotlin将返回null ,而不是抛出ClassCastException 。
问号在elvis操作中的应用
最后,问号也被用于Kotlin的elvis操作符(?:),用来编写一个条件表达式:
var myStr: String? = null
var myVar: String = myStr ?: "Hello"
现在你已经知道了问号是如何在Kotlin中用于空值安全功能的。干得好!👍