0

在下面的例子中,我对?and的用法有点困惑。!!

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

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

4

4 回答 4

4

TL;博士:

?.操作员是安全的。当您不确定链可空性时使用它。
!!.运算符在您确定前一个链运算结果不为空时使用。否则,崩溃。

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

介绍

在 Kotlin 中编码时,您已经达到了最好的(也是最有用的)要点之一。

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

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

目的

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

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

操作员?.

Kotlin 安全呼叫操作员文档

该操作员是安全呼叫操作员。
如果你在调用链中使用它,它会检查你的代码链是否进入下一个元素,只要前一个元素不为空。否则,从语句中返回 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 Double-Bang 操作员文档

这是可怕的双键运算符。谈到语法,它与 非常相似?.,因为它在同一个地方使用。

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

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

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

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

如果位置是,前面的代码可能会崩溃

使用哪一个

两个运算符都是类型转换的。它们都将可空值转换为不可空值。做事的方式是变化的因素。

作为一般规则,如果您确定要定位的值不为 null,请使用!!.,否则坚持使用?.

于 2019-12-20T14:31:52.310 回答
1

如果您将变量定义为

var myFirstVar:String? = null

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

myFirstVar!!.toString

在这里你说你 100% myFirstVar 不会为空(也许你在调用它之前给了它一个值)

但如果你使用 ?

myFirstVar?.toString

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

于 2019-12-20T14:05:33.510 回答
1

如果变量被声明为可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 安全性无关。

于 2019-12-20T14:23:46.880 回答
1

举个例子

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

于 2019-12-20T15:17:19.480 回答