之间有什么区别!和?在科特林的无效安全中?

时间:2019-12-20 13:35:01

标签: android kotlin kotlin-null-safety

在以下情况下,我对?!!的用法有些困惑。

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

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

4 个答案:

答案 0 :(得分:1)

如果您将变量定义为

var myFirstVar:String? = null

这意味着“ myFirstVar”可以为空值,当您使用“ myFirstVar”时,应指出其是否为空值

myFirstVar!!.toString

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

但是,如果您使用?

myFirstVar?.toString

您表示myFirstVar可能具有空值,即?? myFirstVar是否为null时将执行操作,如果为null,则它将不将其转换为字符串(应用程序不会崩溃);如果不是,则将其转换为字符串,这是安全检查以减少null崩溃。

答案 1 :(得分: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,而不是!!?

特殊情况:您可以:

private lateinit var myButton: Button

如果您从不评估myButton,那么您将永远不会拥有KNPE,但是UninitializedPropertyException却与无效威胁或无效安全性无关。

答案 2 :(得分:1)

TL; DR:

?.运算符很安全。当您不确定链的可为空性时,请使用它。 当您确定先前的链运算结果不为空时,
!!.运算符仅用于 。否则,将崩溃。

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

简介

在Kotlin中进行编码时,您遇到了最好(也是最有用)的点之一。

  

我应该在哪使用空安全操作员?

这取决于您要实现的行为。 在Kotlin中,您必须非常明确要使用null值,因为这种语言的设计是开箱即用的,可以是null安全的。 当然,针对JVM会给编程语言带来很多挑战。具有空值的可能性就是其中之一,正如我们将看到的,Kotlin将以一种非常聪明的方式来处理它。

目的

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

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

操作员?.

Kotlin Safe Call Operator Docs

此接线员是安全呼叫接线员。
如果在调用链中使用它,则它将检查您的代码链是否进入下一个元素,即使前一个元素不为null。否则,从该语句重新调整null。

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

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

  

location可以为null或不为null。
我们只是不知道它将是什么,而且Kotlin环境严格确保您要处理该值为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 Operator Docs

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

以一种非常简单的方式对其进行描述:如果调用之前的任何内容为null,则您的代码将崩溃。即刻。没有警告。 在!!.中,您明确地说是

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

但是对于主流软件而言,此功能仅适用于特定用途和狭义用途:如果您100%确保上一个呼叫为 null,请继续。

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

如果位置为

,则先前的代码可能会崩溃

要使用哪个

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

通常,如果您确定要定位的值不为null,请使用!!.,否则请坚持?.

答案 3 :(得分: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());
}

因此,如果var a?.trim()为空,a将不执行任何操作
因此,如果var a!!.trim()为空,则a将引发异常