当前位置:  开发笔记 > 编程语言 > 正文

在Kotlin中,处理可空值,引用或转换它们的惯用方法是什么

如何解决《在Kotlin中,处理可空值,引用或转换它们的惯用方法是什么》经验,为你挑选了1个好方法。

如果我有一个可空类型Xyz?,我想引用它或将其转换为非可空类型Xyz.在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是允许的,为什么?

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

假设我确定它确实从未如此,如何null在不需要if检查的情况下更改或处理值null?例如,在这里,我从地图中检索一个我可以保证存在的值而结果get()不是null.但是我有一个错误:

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?.因此,强制值的类型不可为空的最佳方法是什么?

注意: 这个问题是由作者故意编写和回答的(自答案问题),因此对于常见问题的Kotlin主题的惯用答案存在于SO中.还要澄清为Kotlin的alphas写的一些非常古老的答案,这些答案对于当前的Kotlin来说是不准确的.



1> Jayson Minar..:

首先,您应该阅读Kotlin中关于Null Safety的所有内容,其中包含了彻底的案例.

在Kotlin中,你不能在不确定它的情况下访问可空值null(在条件中检查null),或断言它肯定没有null使用!!确定的运算符,使用?.安全调用访问它,或者最后给出一些可能null是使用?:Elvis运算符的默认值.

对于您在问题中的第一个案例,您可以根据您将使用其中一个的代码的意图进行选择,并且所有这些都是惯用的但具有不同的结果:

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检查时有用",请阅读下面关于智能演员表的背景信息.

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

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

或者在另一种情况下,当地图COULD返回null但你可以提供默认值时,那么Map它本身就有一个getOrElse方法:

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

背景资料:

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

更多关于!!确定的运营商

!!运营商声称,该值不是null或抛出NPE.这应该在开发人员保证价值永远不会出现的情况下使用null.把它想象成一个断言,接着是一个聪明的演员.

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检查保护对可空类型的访问,编译器将智能地将语句体内的值转换为不可为空.有一些复杂的流程,这是不可能发生的,但对于常见的情况工作正常.

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

或者,如果您is检查非可空类型:

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

对于同时安全施放的'when'表达式也是如此:

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

// or

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

有些事情不允许null检查智能转换以便以后使用变量.上面的例子使用的是绝不可能在应用程序的流量已经变异,不管是在本地变量valvar此变量没有机会变异成null.但是,在编译器无法保证流分析的其他情况下,这将是一个错误:

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检查不能智能地转换为非可空值.请参阅下面的"安全呼叫"主题以获取解决方法.

另一个智能强制转换不能信任的情况是val具有自定义getter的对象上的属性.在这种情况下,编译器无法查看变量值的内容,因此您将收到错误消息:

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


有关?.Safe Call运算符的更多信息

如果左边的值为null,则安全调用操作符返回null,否则继续评估右边的表达式.

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

另一个例子,你想要迭代一个列表,但只有当不是null而不是空时,安全调用操作符再次派上用场:

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

在上面的一个例子中,我们有一个案例,我们做了一个if检查,但有可能另一个线程突变了值,因此没有聪明的演员.我们可以更改此示例以使用安全调用操作符以及let函数来解决此问题:

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:时,Elvis运算符允许您提供替代值:

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

它也有一些创造性的用途,例如在某些东西时抛出异常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有一系列功能,可以很好地与上面提到的运算符一起工作.例如:

// 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有道理.一些指导思考:

在某些情况下,它保证不同的返回类型,包括方法调用的状态和成功时的结果.像Result这样的库可以为您提供成功或失败的结果类型,也可以为您的代码分支.Kotlin的Promises图书馆叫做Kovenant,以承诺的形式做同样的事情.

对于集合而言,返回类型始终返回空集合而不是a null,除非您需要第三个"不存在"状态.Kotlin有辅助函数,例如emptyList()emptySet()创建这些空值.

当使用返回可以为您具有默认值或替代值的可空值的方法时,请使用Elvis运算符提供默认值.在的情况下Map使用getOrElse(),它允许一个默认值,以生成代替Map方法get(),它返回一个空值.同样的getOrPut()

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

查看可以帮助的功能,例如for String?.isNullOrEmpty()String?.isNullOrBlank()哪些功能可以安全地运行在可空的值上,并按照您的期望行事.实际上,您可以添加自己的扩展来填补标准库中的任何空白.

断言函数checkNotNull()requireNotNull()标准库中的一样.

辅助函数,例如filterNotNull()从集合中删除空值,或listOfNotNull()从可能的null值返回零或单个项目列表.

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

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有