我这里有一段很常见的 kotlin 代码:
fusedLocationProviderClient.lastLocation.addOnSuccessListener { location: Location? ->
val geoCoder = Geocoder(this, Locale.getDefault())
val addr = geoCoder.getFromLocation(location.latitude, location.longitude, 1)
Toast.makeText(this, "Got location info", Toast.LENGTH_SHORT).show()
locationText.text = addr.get(0).toString()
}.addOnFailureListener { exception: Exception ->
locationText.text = exception.message
}
由于在某些情况下可以null
回调中的位置值,因此我们必须将其定义为nullable
类型,例如location: Location?
.当我想使用位置对象中的纬度和经度属性(如location.longitude
(时,就会出现问题。编译器给出以下错误:
Only safe (?.) or non-null asserted calls are allowed on nullable reciever of type Locaion?
现在我知道做location!!.latitude
是一件危险的事情,因为它声称值不为空。使用location?.latitude
会给我一个类型不匹配Required: Double Found: Double?
我在进行异步调用时多次看到这种情况发生。我的问题是,处理此类情况的官方"kotin"方法是什么?
与 Java 和许多其他语言不同,Kotlin 要求您显式地将可为空的值标识为可为空的值,否则它将引发编译器错误(正如您所发现的那样(。这样做是为了防止可怕的NullPointerException,这是所谓的十亿美元错误的影响(世界各地的程序员在假设变量的值不为null之前忘记检查null,并导致NPE(。
有三种方法可以处理您在 Kotlin 中询问的内容,它归结为您的风格偏好以及关于有多少代码取决于值的 null/non-null 状态。这些方法都包含一个空检查。
出于此答案的目的,我将假设如果 latitude 属性为 null,则您希望抛出错误。但是 Kotlin 中显式可空性的一大优点是它迫使你考虑你实际上想要如何处理空值(这是一个停止代码的错误吗?你是否插入默认值并继续你的代码?等等(。
首先,您可以使用传统的if/else
。出于个人审美原因,我倾向于不在 Kotlin 中使用它。我只是喜欢使用其他方法,因为它们对我来说是"新的",来自类 C 语言,其中 if/else 是旧帽子。
val myVal: Location? = someValue
if(myVal == null) {
throw IllegalStateException("myVal is null")
} else {
/* handle the non-null case(s) */
}
其次,您可以使用 Kotlin 作用域函数之一(它们都以微妙不同的方式使用,但可以根据您的问题互换(。
myValue.let
告诉 Kotlin 将myValue
粘贴到以下代码块中,但重命名为it
。但是myValue?.let
告诉 Kotlin只有在 myValue 为非空时才这样做。如果 myValue 为 null,则let
后面的代码块将不会执行。如果你在代码块后面放一个?:
和一些代码,那么如果myValue为null,它将运行:
val myVal: Location? = someValue
myVal?.let { location ->
/* handle the non-null case */
} ?: throw IllegalStateException("myVal is null")
在上面,throw ...
仅在 myVal 为空时执行。如果 myVal 是非空的,它不会抛出。
第三个选项与第二个选项类似:
val myVal: Location? = someValue
myVal ?: throw IllegalStateException("myVal is null")
/* now handle the non-null case */
还记得上面的?:
吗?再次如此,它以相同的方式运行:如果myVal
为 null,它将执行它右侧的内容。在这种情况下,投掷。例如,如果这是在函数中,您也可以执行myVal ?: return someDefaultValue
。
它作为类型保护运行,并确保对于任何后续代码,myVal不能为空(因为 myVal 是不可变的,我们现在已经确定它不是空的(。
对于可为空的变量,您需要检查第一个变量是否为空。
您可以使用以下方法:
if(location != null){
val latitude = location.latitude
}
or
location?.let{
val latitude = it.latitude // location can be replace with **it**
}