在以下情况下,我对?
和!!
的用法有些困惑。
lat = mLastLocation?.latitude.toString()
longi = mLastLocation!!.longitude.toString()
我应该使用哪个null安全运算符?
答案 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)
?.
运算符很安全。当您不确定链的可为空性时,请使用它。
当您确定先前的链运算结果不为空时,
!!.
运算符仅用于 。否则,将崩溃。
如果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
将引发异常