和有什么区别!!和 ?在科特林的空安全?

What is difference between !! and ? in kotlin's null safety?

我对以下实例中 ?!! 的用法有点困惑。

  lat = mLastLocation?.latitude.toString()
  longi = mLastLocation!!.longitude.toString()

我应该使用哪个空安全运算符?

如果您将变量定义为

var myFirstVar:String? = null

这意味着 "myFirstVar" 可以有一个空值,当你使用 "myFirstVar" 时你应该指出它是否有一个空值

myFirstVar!!.toString

在这里你说你 100% 确定 myFirstVar 不会为 null(也许你在调用它之前已经给它赋值)

但是如果你使用 ?

myFirstVar?.toString

你是说 myFirstVar 可能有一个空值, ?如果 myFirstVar 为 null 或不为 null 将检查,如果是则不会将其转换为字符串(应用程序不会崩溃)如果不是则将其转换为字符串,这是减少 null 的安全检查崩溃。

如果变量被声明为nullable 类型,您在使用它时有两种选择。 让我们以此为例:

private var myButton: Button? = null

所以 myButton 你有两个选择。您可以评估它,或保持原样。但是 运行 上的程序不知道你之前对这个变量做了什么。因此,为了安全起见,Kotlin 语言为您提供了 ?!! 运算符。一是为了程序的安全,不会崩溃导致KNPE:

 myButton?.setOnClickListener{

}

如果按钮为空,应用程序不会崩溃。现在,如果您 100% 确定已使用不同于 null 的值对 Button 求值,则可以使用 !!:

myButton!!.setOnClickListener{

   }

在这种情况下,如果您 运行 程序并且 myButton 为空,您将崩溃。

零安全神话

但是,这不是一个无效的安全案例(我猜)。人们在 Kotlin 中所说的 null safety 的意思正是这样的:

private val myButton: Button = someButtonInitialization()

如果你将它计算为 null,编译器会冲你大喊大叫,因为 Button 是一个不可 null 的类型。否则它将是 Button?

这是 IMO 的空安全,而不是 !!?

特例:你可以有:

private lateinit var myButton: Button

如果你从不评估 myButton 你永远不会有 KNPE,而是 UninitializedPropertyException 与空威胁或空安全无关。

长话短说:

?. 运营商是安全的。当您不确定链的可空性时使用它。
!!. 运算符旨在 只有 当您确定先前的链操作结果不为空时才使用。否则崩溃。

如果 mLastLocation 永远不会为 null,请放心使用 !!.(并重新考虑一下您的代码),否则,请使用 ?.

简介

您在使用 Kotlin 编写代码时达到了最好(也是最有用)的要点之一。

here which null safety operator I should use?

这取决于您想要实现的行为。 在 Kotlin 中,您必须非常具体说明您想对空值执行的操作,因为该语言被设计为开箱即用的空值安全。 当然,以 JVM 为目标会给编程语言带来 许多 挑战。具有空值的可能性就是其中之一,正如我们将看到的,Kotlin 以一种非常聪明的方式处理这个问题。

目的

我们可以解释这两个运算符背后的完整理论,但我相信您真正需要的只是一个示例。
假设您有一个名为 Location 的 class,我们将在可为 null 的变量中声明它。 在 Kotlin 中,这表示为 val location: Location? 我们还假设 Location class 有一个名为 lat 的 属性,它是一个可为 null 的字符串,还有一个 lon 不可为 null 的字符串。

data class User(val lat: String?, val lon: String)

运算符?.

Kotlin Safe Call Operator Docs

该接线员是安全呼叫接线员。
如果您在调用链中使用它,它会检查您的代码链是否进入下一个元素,前提是前一个元素不为空。否则,从语句返回 null。

val location: Location? = getLocation()
println(location.lat)      // Compile-time error.
println(location?.lat)    // Works fine.

发生这种情况是因为在第一种情况下,?. 之前的对象是可为空的,因此 Kotlin 编译器推断访问可为空的 属性 会导致 NPE。

location could be null or not-null.
We just don't know what it will be, and the Kotlin environment strictly makes sure that you are handling the eventuality of that value being null, as the type of our references variable is defined as nullable.

但是,您的开发人员可能不知道某个变量为空。有时甚至不能由您决定接收空值还是非空值。

在这种情况下,您可以安全地坚持使用 ?,如果您不确定所引用的内容是否为 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 Double-Bang Operator Docs

这是可怕的双爆炸运算符。 说到语法,它和 ?. 非常相似,因为它用在同一个地方。

用一种非常简单的方式来描述它:如果调用之前的任何东西都是空的,你的代码就会崩溃。即刻。没有警告。 使用 !!. 你明确地说

Kotlin 必须忽略任何类型可空性标记并执行您想要的操作,即使它进入了某种危险区域。 这被称为力,因为您迫使 Kotlin 环境相信前面的语句不为空。 此运算符的最佳用例是在将另一个库移植到 Kotlin 时,或在处理 API RESTful 响应时,可能会出现空值的情况,但由于 environment/platform 原因,您知道一些值不能为空。如果使用得当,这可以帮助您首先在 Kotlin 世界中实现类型安全。

但是对于主流软件来说,这个特性是为了一个非常具体和狭窄的用途:如果你 100% 确定之前的调用是 not null,那么继续。

val location: Location? = getLocation()
println(location!!.lon)

如果位置为

,之前的代码可能会崩溃

使用哪个

两个运算符都是类型转换。它们都将可为 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());
}

因此,如果 var a 为空,a?.trim() 将不执行任何操作
因此,如果 var a 为 null

a!!.trim() 将抛出异常