如果我有一个可空类型 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()
}
如果不确定 if
,我如何更改或处理值不是 null
,假设我确定它确实永远不会 null
?例如,这里我从 Map 中检索一个我可以保证存在的值,而 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?
. 因此,强制值的类型不可为空的最佳方法是什么?
Note: 这个问题是由作者故意编写和回答的(Self-Answered Questions),因此对于常见问题的Kotlin主题的惯用答案存在于SO中 . 还要澄清为Kotlin的alphas写的一些非常古老的答案,这些答案对于当前的Kotlin来说是不准确的 .
2 回答
首先,你应该阅读Kotlin的所有关于Null Safety的内容 .
在Kotlin中,你不能在不确定它不是
null
(Checking for null in conditions)的情况下访问可空值,或者断言它肯定不是null
使用!! sure operator,用?. Safe Call访问它,或者最后使用null
使用null
使用默认值?: Elvis Operator .For your 1st case in your question 你有选择,取决于你将使用其中一个代码的意图,并且所有都是惯用的,但有不同的结果:
对于"Why does it work when null checked",请阅读smart casts下面的背景信息 .
For your 2nd case in your question 在
Map
的问题中,如果您作为开发人员确定结果永远不会null
,请使用!!
sure运算符作为断言:或者在另一种情况下,当 Map COULD返回null但您可以提供默认值时,
Map
本身有一个getOrElse method:背景资料:
Note: 在下面的示例中我使用显式类型来清除行为 . 对于类型推断,通常可以省略局部变量和私有成员的类型 .
更多关于!!确定的运营商
!!
运算符声明该值不是null
或抛出NPE . 这应该用于开发人员保证该值永远不会是null
的情况 . 把它想象成一个断言,然后是smart cast .阅读更多:!! Sure Operator
有关null检查和智能投射的更多信息
如果使用
null
检查保护对可空类型的访问,则编译器将smart cast语句正文中的值不可为空 . 有一些复杂的流程,这是不可能发生的,但对于常见的情况工作正常 .或者,如果您对非可空类型执行
is
检查:对于同时安全施放的'when'表达式也是如此:
有些事情不允许
null
检查smart cast以便稍后使用该变量 . 上面的示例使用的局部变量在应用程序的流程中决不会发生变异,无论是val
还是var
这个变量都没有机会变异为null
. 但是,在编译器无法保证流分析的其他情况下,这将是一个错误:变量
nullableInt
的生命周期不完全可见,可以从其他线程分配,null
检查不能smart cast为非可空值 . 有关解决方法,请参阅下面的"Safe Calls"主题 .另一个不能被smart cast信任而不变异的情况是具有自定义getter的对象上的
val
属性 . 在这种情况下,编译器无法查看改变值的内容,因此您将收到错误消息:阅读更多:Checking for null in conditions
更多关于?安全呼叫运营商
如果左边的值为null,则安全调用操作符返回null,否则继续评估右边的表达式 .
另一个例子,你想要迭代一个列表,但只有当不是
null
而不是空时,安全调用操作符再次派上用场:在上面的一个例子中,我们有一个案例,我们做了
if
检查,但有可能另一个线程突变了值,因此没有smart cast . 我们可以更改此示例以使用安全调用运算符和let
函数来解决此问题:阅读更多:Safe Calls
关于?:猫王操作员的更多信息
Elvis运算符允许您在运算符左侧的表达式为
null
时提供替代值:它也有一些创造性的用途,例如当某些东西是_269754时抛出异常:
或者从函数中提前返回:
阅读更多:Elvis Operator
具有相关函数的空操作符
Kotlin stdlib有一系列功能,可以很好地与上面提到的运算符一起工作 . 例如:
相关主题
在Kotlin中,大多数应用程序都试图避免
null
值,但并不总是可行 . 有时null
非常有意义 . 一些指导思考:在某些情况下,它保证不同的返回类型,包括方法调用的状态和成功时的结果 . 像Result这样的库为您提供了成功或失败的结果类型,它也可以分支您的代码 . Kotlin的Promises库叫做Kovenant以承诺的形式做同样的事情 .
for collections作为返回类型总是返回一个空集合而不是
null
,除非你需要第二个状态"not present" . Kotlin有辅助函数,如emptyList() or emptySet()来创建这些空值 .当使用返回可以为您具有默认值或替代值的可空值的方法时,请使用Elvis运算符提供默认值 . 在
Map
的情况下,使用getOrElse(),它允许生成默认值而不是Map
方法get()
,它返回一个可为空的值 . getOrPut()相同当从Kotlin不能确定Java代码的可空性的Java中重写方法时,如果您确定签名和功能应该是什么,则总是可以从覆盖中删除
?
可空性 . 因此,您的重写方法更安全null
. 与在Kotlin中实现Java接口相同,将可空性更改为您知道的有效 .查看可以帮助的函数,例如String?.isNullOrEmpty()和String?.isNullOrBlank(),它们可以安全地操作可空值并按预期执行 . 实际上,您可以添加自己的扩展来填补标准库中的任何空白 .
断言函数类似于标准库中的checkNotNull()和requireNotNull() .
辅助函数,如filterNotNull(),用于从集合中删除空值,或者listOfNotNull()用于从可能的
null
值返回零或单个项目列表 .还有一个Safe (nullable) cast operator,如果不可能,允许转换为非可空类型返回null . 但是我没有一个有效的用例,这个用例没有通过上面提到的其他方法解决 .
之前的答案是一个难以遵循的行为,但这是一个快速简便的方法:
如果它真的永远不会为空,那么异常就不会发生,但是如果有的话,你会看到出了什么问题 .