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中用于空值安全功能的。干得好!👍