在Kotlin中,处理可空值,引用或转换它们的惯用方式是什么 [英] In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them

查看:99
本文介绍了在Kotlin中,处理可空值,引用或转换它们的惯用方式是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我具有可为null的类型Xyz?,我想引用它或将其转换为不可为null的类型Xyz.在Kotlin中,惯用的方式是什么?

If I have a nullable type Xyz?, I want to reference it or convert it to a non-nullable type Xyz. What is the idiomatic way of doing so in Kotlin?

例如,此代码有误:

val something: Xyz? = createPossiblyNullXyz()
something.foo() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Xyz?"

但是如果我先检查为null,那是为什么?

But if I check null first it is allowed, why?

val something: Xyz? = createPossiblyNullXyz()
if (something != null) {
    something.foo() 
}

假设我确定它绝对不是null,那么如何在不要求if检查的情况下将值更改为非null或将其视为非null?例如,在这里我从一个映射中检索一个可以保证存在的值,而get()的结果不是null.但是我有一个错误:

How do I change or treat a value as not null without requiring the if check, assuming I know for sure it is truly never null? For example, here I am retrieving a value from a map that I can guarantee exists and the result of get() is not null. But I have an error:

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")
something.toLong() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?"

方法get()认为该项目可能丢失,并返回类型Int?.因此,强制值类型不可为空的最佳方法是什么?

The method get() thinks it is possible that the item is missing and returns type Int?. Therefore, what is the best way to force the type of the value to be not nullable?

注意: 该问题是作者故意写并回答的(

Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO. Also to clarify some really old answers written for alphas of Kotlin that are not accurate for current-day Kotlin.

推荐答案

首先,您应该阅读有关

First, you should read all about Null Safety in Kotlin which covers the cases thoroughly.

在Kotlin中,您不能访问可为空的值,而不能确保它不是null(null docs/reference/null-safety.html#the--operator"rel =" noreferrer> !!确定运算符,并通过

In Kotlin, you cannot access a nullable value without being sure it is not null (Checking for null in conditions), or asserting that it is surely not null using the !! sure operator, accessing it with a ?. Safe Call, or lastly giving something that is possibly null a default value using the ?: Elvis Operator.

对于您遇到的第一种情况,根据代码的意图,您可以选择以下选项之一,它们都是惯用的,但结果不同:

For your 1st case in your question you have options depending on the intent of the code you would use one of these, and all are idiomatic but have different results:

val something: Xyz? = createPossiblyNullXyz()

// access it as non-null asserting that with a sure call
val result1 = something!!.foo()

// access it only if it is not null using safe operator, 
// returning null otherwise
val result2 = something?.foo()

// access it only if it is not null using safe operator, 
// otherwise a default value using the elvis operator
val result3 = something?.foo() ?: differentValue

// null check it with `if` expression and then use the value, 
// similar to result3 but for more complex cases harder to do in one expression
val result4 = if (something != null) {
                   something.foo() 
              } else { 
                   ...
                   differentValue 
              }

// null check it with `if` statement doing a different action
if (something != null) { 
    something.foo() 
} else { 
    someOtherAction() 
}

对于为什么检查为null时为什么起作用",请阅读以下智能投射.

For the "Why does it work when null checked" read the background information below on smart casts.

对于您在问题中的第二种情况,在与Map有关的问题中,如果您作为开发人员确定结果永远不会为null,请使用!! sure运算符作为断言:

For your 2nd case in your question in the question with Map, if you as a developer are sure of the result never being null, use !! sure operator as an assertion:

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")!!
something.toLong() // now valid

或在另一种情况下,当地图可以返回空值但您可以提供默认值时,则Map本身具有

or in another case, when the map COULD return a null but you can provide a default value, then Map itself has a getOrElse method:

val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.getOrElse("z") { 0 } // provide default value in lambda
something.toLong() // now valid


背景信息:

注意: 在下面的示例中,我使用显式类型来使行为更清晰.通过类型推断,通常可以省略局部变量和私有成员的类型.


Background Information:

Note: in the examples below I am using explicit types to make the behavior clear. With type inference, normally the types can be omitted for local variables and private members.

!!运算符断言该值不是null或引发NPE.如果开发人员保证该值永远不会为null,则应使用此方法.将其视为断言,后跟智能演员.

The !! operator asserts that the value is not null or throws an NPE. This should be used in cases where the developer is guaranteeing that the value will never be null. Think of it as an assert followed by a smart cast.

val possibleXyz: Xyz? = ...
// assert it is not null, but if it is throw an exception:
val surelyXyz: Xyz = possibleXyz!! 
// same thing but access members after the assertion is made:
possibleXyz!!.foo()

了解更多:!确定运算符

如果通过null检查保护对可为null的类型的访问,则编译器将

If you protect access to a nullable type with a null check, the compiler will smart cast the value within the body of the statement to be non-nullable. There are some complicated flows where this cannot happen, but for common cases works fine.

val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
   // allowed to reference members:
   possiblyXyz.foo()
   // or also assign as non-nullable type:
   val surelyXyz: Xyz = possibleXyz
}

或者如果您对非空类型进行is检查:

Or if you do a is check for a non-nullable type:

if (possibleXyz is Xyz) {
   // allowed to reference members:
   possiblyXyz.foo()
}

与"when"表达式一样,这些表达式也可以安全转换:

And the same for 'when' expressions that also safe cast:

when (possibleXyz) {
    null -> doSomething()
    else -> possibleXyz.foo()
}

// or

when (possibleXyz) {
    is Xyz -> possibleXyz.foo()
    is Alpha -> possibleXyz.dominate()
    is Fish -> possibleXyz.swim() 
}

有些事情不允许null检查以智能投射供以后使用该变量.上面的示例使用了一个局部变量,无论该变量是val还是var,该局部变量都不会在应用程序的流程中发生突变,而该变量没有机会突变为null.但是,在其他情况下,编译器不能保证流分析,这将是一个错误:

Some things do not allow the null check to smart cast for the later use of the variable. The example above uses a local variable that in no way could have mutated in the flow of the application, whether val or var this variable had no opportunity to mutate into a null. But, in other cases where the compiler cannot guarantee the flow analysis, this would be an error:

var nullableInt: Int? = ...

public fun foo() {
    if (nullableInt != null) {
        // Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
        val nonNullableInt: Int = nullableInt
    }
}

变量nullableInt的生命周期并不完全可见,并且可以从其他线程分配,null检查不能为

The lifecycle of the variable nullableInt is not completely visible and may be assigned from other threads, the null check cannot be smart cast into a non-nullable value. See the "Safe Calls" topic below for a workaround.

智能投射不能信任的另一种情况mutate是具有自定义getter的对象的val属性.在这种情况下,编译器无法查看更改值的原因,因此您将收到错误消息:

Another case that cannot be trusted by a smart cast to not mutate is a val property on an object that has a custom getter. In this case, the compiler has no visibility into what mutates the value and therefore you will get an error message:

class MyThing {
    val possibleXyz: Xyz? 
        get() { ... }
}

// now when referencing this class...

val thing = MyThing()
if (thing.possibleXyz != null) {
   // error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
   thing.possiblyXyz.foo()
}

了解更多信息:检查条件是否为空

如果左侧的值为null,则安全调用运算符将​​返回null,否则继续评估右侧的表达式.

The safe call operator returns null if the value to the left is null, otherwise continues to evaluate the expression to the right.

val possibleXyz: Xyz? = makeMeSomethingButMaybeNullable()
// "answer" will be null if any step of the chain is null
val answer = possibleXyz?.foo()?.goo()?.boo()

另一个要迭代列表但仅当不是null并且不为空的示例时,安全调用运算符再次派上用场:

Another example where you want to iterate a list but only if not null and not empty, again the safe call operator comes in handy:

val things: List? = makeMeAListOrDont()
things?.forEach {
    // this loops only if not null (due to safe call) nor empty (0 items loop 0 times):
}

在上面的一个示例中,我们有一个案例,我们进行了if检查,但是有机会另一个线程对该值进行了更改,因此没有

In one of the examples above we had a case where we did an if check but have the chance another thread mutated the value and therefore no smart cast. We can change this sample to use the safe call operator along with the let function to solve this:

var possibleXyz: Xyz? = 1

public fun foo() {
    possibleXyz?.let { value ->
        // only called if not null, and the value is captured by the lambda
        val surelyXyz: Xyz = value
    }
}

了解更多信息:安全呼叫

Elvis运算符允许您在运算符左侧的表达式为null时提供替代值:

The Elvis operator allows you to provide an alternative value when an expression to the left of the operator is null:

val surelyXyz: Xyz = makeXyzOrNull() ?: DefaultXyz()

它也有一些创造性的用途,例如当null时抛出异常:

It has some creative uses as well, for example throw an exception when something is null:

val currentUser = session.user ?: throw Http401Error("Unauthorized")

或从函数中提前返回

fun foo(key: String): Int {
   val startingCode: String = codes.findKey(key) ?: return 0
   // ...
   return endingValue
}

了解更多信息:猫王操作员

Kotlin stdlib具有一系列功能,这些功能可以很好地与上述运算符配合使用.例如:

Kotlin stdlib has a series of functions that work really nicely with the operators mentioned above. For example:

// use ?.let() to change a not null value, and ?: to provide a default
val something = possibleNull?.let { it.transform() } ?: defaultSomething

// use ?.apply() to operate further on a value that is not null
possibleNull?.apply {
    func1()
    func2()
}

// use .takeIf or .takeUnless to turn a value null if it meets a predicate
val something = name.takeIf { it.isNotBlank() } ?: defaultName

val something = name.takeUnless { it.isBlank() } ?: defaultName


相关主题

在Kotlin中,大多数应用程序都尝试避免使用null值,但这并不总是可能的.有时null很有道理.要考虑的一些准则:


Related Topics

In Kotlin, most applications try to avoid null values, but it isn't always possible. And sometimes null makes perfect sense. Some guidelines to think about:

    在某些情况下,
  • 保证使用不同的返回类型,包括方法调用的状态和成功的结果.诸如 Result 之类的库为您提供了成功或失败的结果类型,它们也可以分支您的代码. Kotlin的Promises库称为 Kovenant 也是以Promise的形式进行的.

  • in some cases, it warrants different return types that include the status of the method call and the result if successful. Libraries like Result give you a success or failure result type that can also branch your code. And the Promises library for Kotlin called Kovenant does the same in the form of promises.

总是返回空集合而不是null,除非您需要第三种状态不存在". Kotlin具有帮助功能,例如 emptyList()emptySet() 创建这些空值.

for collections as return types always return an empty collection instead of a null, unless you need a third state of "not present". Kotlin has helper functions such as emptyList() or emptySet() to create these empty values.

当使用返回可为空值的方法时,可以使用Elvis运算符提供默认值,该方法可为您提供默认值或替代值.如果是Map,请使用 getOrElse() 允许生成默认值,而不是Map方法get()返回空值的方法.与 getOrPut()

when using methods which return a nullable value for which you have a default or alternative, use the Elvis operator to provide a default value. In the case of a Map use the getOrElse() which allows a default value to be generated instead of Map method get() which returns a nullable value. Same for getOrPut()

当从Kotlin不确定Java代码的可空性的Java重写方法开始时,如果可以确定签名和功能应该是什么,则可以始终从覆盖中删除?可空性.因此,您重写的方法更安全.与在Kotlin中实现Java接口相同,将可空性更改为您知道的有效值.

when overriding methods from Java where Kotlin isn't sure about the nullability of the Java code, you can always drop the ? nullability from your override if you are sure what the signature and functionality should be. Therefore your overridden method is more null safe. Same for implementing Java interfaces in Kotlin, change the nullability to be what you know is valid.

查看已经可以提供帮助的功能,例如

look at functions that can help already, such as for String?.isNullOrEmpty() and String?.isNullOrBlank() which can operate on a nullable value safely and do what you expect. In fact, you can add your own extensions to fill in any gaps in the standard library.

声明功能,例如 checkNotNull() requireNotNull() 标准库.

assertion functions like checkNotNull() and requireNotNull() in the standard library.

帮助器功能,例如 filterNotNull() 从集合中删除空值,或 listOfNotNull() 用于从可能的null值返回零或单个项目列表.

helper functions like filterNotNull() which remove nulls from collections, or listOfNotNull() for returning a zero or single item list from a possibly null value.

有一个安全(可为空)强制转换运算符也允许强制转换为非空类型,如果不可能的话,返回null.但是我没有一个有效的用例,而上面提到的其他方法都无法解决这个用例.

there is a Safe (nullable) cast operator as well that allows a cast to non-nullable type return null if not possible. But I do not have a valid use case for this that isn't solved by the other methods mentioned above.

这篇关于在Kotlin中,处理可空值,引用或转换它们的惯用方式是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆