在下面的例子中,我对?
and的用法有点困惑。!!
lat = mLastLocation?.latitude.toString()
longi = mLastLocation!!.longitude.toString()
我应该使用哪个空安全运算符?
在下面的例子中,我对?
and的用法有点困惑。!!
lat = mLastLocation?.latitude.toString()
longi = mLastLocation!!.longitude.toString()
我应该使用哪个空安全运算符?
?.
操作员是安全的。当您不确定链可空性时使用它。
!!.
运算符仅在您确定前一个链运算结果不为空时使用。否则,崩溃。
如果mLastLocation
永远不会为空,请放心使用!!.
(并且重新考虑一下您的代码),否则,请使用?.
在 Kotlin 中编码时,您已经达到了最好的(也是最有用的)要点之一。
在这里我应该使用哪个空安全运算符?
这取决于您想要实现的行为。在 Kotlin 中,您必须非常具体地说明要对 null 值做什么,因为该语言被设计为开箱即用的 null 安全。当然,针对 JVM 给编程语言带来了许多挑战。具有空值的可能性就是其中之一,正如我们将看到的,Kotlin 以非常聪明的方式处理了这个问题。
我们可以解释这两个运算符背后的完整理论,但我相信你只需要一个例子。
假设您有一个名为 的类,Location
我们将在一个可为空的变量中声明它。在 Kotlin 中,这表示为val location: Location?
假设Location
类有一个名为 的属性lat
,它是一个可为空的字符串和一个lon
不可为空的字符串。
data class User(val lat: String?, val lon: String)
?.
该操作员是安全呼叫操作员。
如果你在调用链中使用它,它会检查你的代码链是否进入下一个元素,只要前一个元素不为空。否则,从语句中返回 null。
val location: Location? = getLocation()
println(location.lat) // Compile-time error.
println(location?.lat) // Works fine.
发生这种情况是因为在第一种情况下,之前的对象?.
是可为空的,因此 Kotlin 编译器推断访问可为空的属性会导致 NPE。
location
可以为空或非空。
我们只是不知道它会是什么,并且 Kotlin 环境严格确保您正在处理该值为 null 的可能性,因为我们的引用变量的类型被定义为可为 null。
但是,您的开发人员可能不知道某个变量为 null。有时,您甚至无法接收空值或非空值。
在这种情况下,您可以放心地坚持使用?
,如果您不确定您所引用的内容是否为空,则知道该运算符是您的朋友。
val location: Location = getSafeLocation()
val nullableLocation: Location? = getLocation()
// Fine, may print "null" or the value, if present.
// println accepts nullable values
println(location.lar)
// 100% sure it'll print the corrisponding String value
println(location.lon)
// May print "null", "null", or the lat String value.
// The first "null" is because the ? operator will check if
// nullableLocation is null. If it is, it stops the execution
// chain and returns null. Otherwise, it assumes nullableLocation is safe
// and goes on.
//
// The second "null" is because the value itself of lat
// is declared as String? and Kotlin knows it may be null.
// If println() did not accept null values, this call would fail,
// but instead it prints "null" in case lat is indeed null.
println(nullableLocation?.lat)
// Since lat was the last element of the chain, it is not
// delivered as the parameter type anymore, and thus if we
// want to read a property from it we have to ensure that it isn't null.
println(nullableLocation?.lat?.length)
// This, as you may guess, returns wither null in case nullableLocation is null,
// otherwise 100% sure it will print lon value, since it is not a String? but a String.
println(nullableLocation?.lon)
!!.
这是可怕的双键运算符。谈到语法,它与 非常相似?.
,因为它在同一个地方使用。
用一个非常简单的方式来描述它:如果调用之前的任何内容为空,你的代码就会崩溃。即刻。没有警告。你!!.
明确地说
Kotlin 必须忽略任何类型的可空性标记并执行您想要的操作,即使它进入了某种危险区域。这被称为强制,因为您强制 Kotlin 环境相信前面的语句不为空。此运算符的最佳用例是在将另一个库移植到 Kotlin 时,或者在处理 API RESTful 响应时,可能会出现空值的情况,但由于环境/平台原因,您知道某些值不能为空。如果使用得当,这可以帮助您首先在 Kotlin 世界中实现类型安全。
但是对于主流软件来说,这个特性是为了一个非常具体和狭隘的用途:如果你 100% 确定之前的调用不为空,那就继续吧。
val location: Location? = getLocation()
println(location!!.lon)
如果位置是,前面的代码可能会崩溃
两个运算符都是类型转换的。它们都将可空值转换为不可空值。做事的方式是变化的因素。
作为一般规则,如果您确定要定位的值不为 null,请使用!!.
,否则坚持使用?.
如果您将变量定义为
var myFirstVar:String? = null
这意味着“myFirstVar”可以有一个空值,当你使用“myFirstVar”时,你应该指出它是否有一个空值
myFirstVar!!.toString
在这里你说你 100% myFirstVar 不会为空(也许你在调用它之前给了它一个值)
但如果你使用 ?
myFirstVar?.toString
你表示 myFirstVar 可能有一个空值,?如果 myFirstVar 是否为空,它将小鸡,如果是,则不会将其转换为字符串(应用程序不会崩溃),如果不是,则将其转换为字符串,这是减少空值的安全检查崩溃。
如果变量被声明为可null
类型,则在使用它时有两种选择。让我们以此为例:
private var myButton: Button? = null
所以你有两个选择myButton
。您可以对其进行评估,也可以保持原样。但是运行中的程序不知道你之前对变量做了什么。所以,为了安全起见,Kotlin 语言为您提供了?
和!!
操作符。一个是为了程序的安全,所以它不会崩溃并导致 KNPE:
myButton?.setOnClickListener{
}
如果按钮为空,应用程序不会崩溃。现在,如果您 100% 确定您已使用与 null 不同的值评估 Button,则可以使用!!
:
myButton!!.setOnClickListener{
}
在这种情况下,如果你运行程序并且myButton
为空,你就会崩溃。
空安全神话
但是,这不是一个空安全案例(我猜)。人们在 Kotlin 中所说的空安全性的含义正是这样的:
private val myButton: Button = someButtonInitialization()
如果您将其评估为null
,编译器会因为Button
它是无能null
类型而对您大喊大叫。否则会这样Button?
。
这是零安全 IMO,而不是!!
or ?
。
特殊情况:您可以拥有:
private lateinit var myButton: Button
如果您从不评估myButton
您将永远不会拥有 KNPE,但这UninitializedPropertyException
与 Null 威胁或 null 安全性无关。
举个例子
var a: String? = "Hello world!"
fun test1() {
a?.trim()
}
fun test2() {
a!!.trim()
}
第一个反编译的函数是:
public static final void test1() {
String var10000 = a;
if (var10000 != null) {
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
}
第二个反编译的函数是:
public static final void test2() {
String var10000 = a;
if (var10000 == null) {
Intrinsics.throwNpe();
}
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
其中Intrinsics.throwNpe();
定义为:
public static void throwNpe() {
throw sanitizeStackTrace(new KotlinNullPointerException());
}
So如果 var 为 nulla?.trim()
则什么都不做 所以如果 var
为nulla
将抛出异常a!!.trim()
a