[英]Null check with ?. vs ?: fails in kotlin
我的代碼中有以下語句:
safeOrderResult.accomplished?.let{ safeAccomplished->
//Do something with safeAccomplished when accomplished <> null
Log.i(TAG,"bind: safeOrderResult.accomplishedId.let?{}")
}?:let{
//Do something when accomplished == null
Log.i(TAG,"bind: safeOrderResult.accomplishedId?:let{} *null*" )
}
在這里,我的代碼做了一些奇怪的事情:
在三星 TAB A(我認為不重要)上,它按預期工作。
在三星 S9 上,它調用了兩個 let 部分。
來自 Logcat 三星 S9 (android 10) 的片段
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId = 408
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId.let?.{}
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: handleDate = null <- inside above let: ok
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId?:let{} *null*
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: flagged = false or null
TabA:android 10
2021-05-06 14:21:16.676 2468-2468/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId = 427
2021-05-06 14:21:16.676 2468-2468/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId.let?.{}
2021-05-06 14:21:16.678 2468-2468/no.norva24.mslam I/ViewHolder: bind: handleDate = null <-- inside above let
2021-05-06 14:21:16.685 2468-2468/no.norva24.mslam I/ViewHolder: bind: flagged = false or null
關鍵是,一個值如何既是 null 又包含一個值?或者 kotlin 可以“更改”為 null 並在第二個“讓”中發生變化不做)
我正在使用 kotlin 1.5.0
編輯 2021.05.06 18:55 GMT+2
我不確定,但我今天可能在這里學到了一些東西:;)
safeOrderResult.accomplished?.let{ safeAccomplished->
//Do something with safeAccomplished when accomplished <> null
/*Here I have preserved my value in safeAccomplished
And actually returning a value below (a Unit()) from Log.i ?*/
Log.i(TAG,"bind: safeOrderResult.accomplishedId.let?{}")
}?:let{
//Do something when accomplished == null
/* But why did the code kick in here ?
After it was inside the let above ? I thought the '?:let' was
continuing if the '?.let' didn't kick in.
*/
Log.i(TAG,"bind: safeOrderResult.accomplishedId?:let{} *null*" )
}
/*
Below is the actual code which had the trouble (the code isn't finished therefore the "preserved" `let` values isn't used)
*/
safeOrderResult.accomplishedId?.let {
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.VISIBLE
listItemOrderListMaterialTextViewOrderStatus.text =
context.resources.getStringArray(
R.array.basic_register_accomplish_status_names)[1]
listItemOrderListMaterialTextViewDate.text =
dateStringSplitSpace(safeOrderResult.registeredDate)
Log.i(TAG, "bind: handleDate = ${safeOrderResult.handleDate}")
listItemOrderListMaterialTextViewReason.text =
if(safeOrderResult.handleDate.isNullOrEmpty())
"Still possible to update"
else
"Assignment locked on ${dateStringSplitSpace(safeOrderResult.handleDate)}"
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
if(safeOrderResult.handleDate.isNullOrEmpty())
R.color.list_item_register_field_accomplished_background
else
R.color.list_item_register_field_accomplished_locked_background
)
)
}
listItemOrderListLinearLayoutCompatStatusMore?.apply {
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
if(safeOrderResult.handleDate.isNullOrEmpty())
R.color.list_item_register_field_accomplished_background
else
R.color.list_item_register_field_accomplished_locked_background
)
)
}
}?:let {
safeOrderResult.passedId?.let { safePassedId->
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.VISIBLE
listItemOrderListMaterialTextViewOrderStatus.text =
context.resources.getStringArray(
R.array.basic_register_accomplish_status_names
)[2]
listItemOrderListMaterialTextViewDate.text =
dateStringSplitSpace(safeOrderResult.registeredDate)
listItemOrderListMaterialTextViewReason.text =
safeOrderResult.passedReason
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
R.color.list_item_register_field_passed_background,
)
)
}
}?:let {
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.GONE
}
}
}
** 附錄 2020.05.06 19:30 GMT+2 **
在操場上,我遇到了麻煩:
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
class A {
fun f() {
let { println(it) }
}
}
data class DataClass(
var value1:String?=null,
var value2:String?=null
)
fun main() {
A().f()
var myData = DataClass()
myData.value1 = "1"
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}?:let{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}?:let{
println("value2==null !!")
}
}
它在?:let
's 上面踢到了哪里。 至少在 kotin v.1.5.0 中這是可以的......
附錄 2:2020.05.06 19:40 GMT+2
所以... dataClass.value?:let{ }
是不允許的嗎? 在“標准”kotlin 方案中檢查 null 是否存在?,但在AS2020.3.1.15 w/kotlin 1.5.0
中仍然“有效” ...
附錄 3:2020.05.06 19:55 GMT+2
當使用另一種方法時(在?:let{
中省略let
關鍵字,我根據上面的操場代碼得到了這個答案:
在這里,我還希望 value2 以value2==null !!
但它沒有...
現在是操場代碼:
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
class A {
fun f() {
let { println(it) }
}
}
data class DataClass(
var value1:String?=null,
var value2:String?=null
)
fun main() {
A().f()
var myData = DataClass()
myData.value1 = "1"
/*
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}?:let{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}?:let{
println("value2==null !!")
}
*/
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}
myData.value1?:{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}
myData.value2?:{
println("value2==null !!")
}
}
……還是有點糊塗……
let
function 確實可以將您的目標更改為 null。 它將目標更改為它返回的任何內容。 lambda 隱式返回其最后一個表達式的結果。 上面的代碼有一個Log.i()
調用作為它的最后一個表達式,所以它返回 Unit,所以如果第一個運行,第二個let
function 不應該運行。 您是否有可能在第一次let
lambda 結束時剪掉了一些可能返回 null 值的代碼?
對上述問題的快速解決方法是將let
替換為also
,因為also
總是返回它的接收者。
我認為最有經驗的 Kotlin 用戶會建議您不要鏈接 scope function 調用,因為它使代碼難以遵循並且很容易引入細微的錯誤。 您可以編寫更強大的版本,如下所示:
val accomplished = safeOrderResult.accomplished
if (accomplished != null) {
//Do something with smart-cast non-nullable accomplished
} else {
//Do something when accomplished == null
}
猜測一下,第一個在最后返回null
,這意味着整個表達式產生的值是null
,所以?:
之后的東西被觸發(因為這是“如果左側評估為空”條件) .
為什么這只會發生在某些三星型號上 - 誰知道,他們有在 Android 庫中弄亂東西的歷史。 我會准確檢查塊中發生的事情以及它可能評估的內容。 您可能需要在最后返回Unit
,或使用 function(如apply
)返回接收器而不是 lambda 的結果。
這就是為什么if/else
更合適的原因——你在一開始就有一個條件,然后你決定是專門做一件事還是另一件事。 let
產生一個值,它通常用於沿鏈傳播一個值,並返回一個結果。 ?:
是最終的默認值,如果結果是 null。
絕對可以運行let
塊和?:
之后的代碼,有時這是您想做的事情。 作為一種構造,它通常用於返回默認值。 所以if/else
更明確地說明了你在做什么以及它是如何工作的,它有助於避免像這樣的意外錯誤!
你可以用?.let
做一個 if-null-else 但在我看來它不是很可讀
var s: String? = "Str"
s?.let { println("A ok") } ?: run { println("A null") }
s = null
s?.let { println("B ok") } ?: run { println("B null") }
A ok
B null
也可以引入像這樣的細微錯誤:
var s: String? = "Str"
s?.let { println("A ok"); null } ?: run { println("A null") }
A ok
A null
這就是為什么如果您都需要非空和 null 案例,您應該使用 if-else 。 ( ?.
適用於只有非空情況才有意義的情況):
if (s == null) println("A null") else println("A ok")
if (s == null) {
println("A null")
} else {
println("A ok")
}
如果您不想像@Tenfour04 的回答那樣將accomplished
綁定到變量,我會將其寫為
safeOrderResult.accomplished.let {
if (it != null) {
// safeOrderResult.accomplished was not null, use it
} else {
// safeOrderResult.accomplished was null
}
}
或者
safeOrderResult.accomplished.let { accomplished ->
if (accomplished != null) {
// safeOrderResult.accomplished was not null, use accomplished
} else {
// safeOrderResult.accomplished was null
}
}
注意.let
而不是?.let
。 但是選擇可讀性/方便性。 我絕對不會用
value?.let{ safeValue-> /*dowork*/}; value?:let{/*do null work*/}
正如您在另一條評論中所建議的那樣。
感謝上面的許多好答案,你們都是對的......
我為我的問題找到了以下解決方案,但仍然不太高興:
我使用.apply
刪除一些value.
高架,
safeOrderResult.apply{
if(accomplished!=null){
//Do something with accomplished since accomplished <> null
Log.i(TAG,"bind: accomplished != null")
}else{
//Do something when accomplished == null
Log.i(TAG,"bind: accomplished == null" )
}
}
我標記接受@Alexey Romanov 的建議,這是非常合理的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.