我正在将一个项目从java迁移到kotlin,该项目使用了许多可以为null的变量,在与用户或外部作业进行一些交互之前,这些变量不会初始化。
我试图使用kotlin null安全优势,并且我试图避免在源代码中使用null
,所以我使用lateinit
处理所有变量,如下所示:lateinit var location: Location
而不是使用var location: Location? = null
我这样做是为了避免每次需要使用这些变量时都使用?
。现在的问题是…如果变量没有初始化会发生什么?
例如,如果我需要在代码的某个部分调用location.getX()
,会发生什么?如何处理位置仍未初始化的情况?
我想用这个来处理它:
if (!this::location.isInitialized){
location.getX()
}
但是,我用isInitialized等的样板代码填充代码……所以……最后,我更喜欢没有null安全性的项目的旧java版本,它更清晰、更简短。
如果我回到var location: Location? = null
,那么,我需要使用
location.let {
it.getX()
}
或
location?.getX()
在第二种情况下,我仍然在处理null,这是我试图避免的事情
我做错什么了吗?我必须回到空变量和var location: Location? = null
而不是使用lateinit
吗?有更好的方法吗?
我认为您对如何在Kotlin中使用null有一个错误的概念。正是因为Kotlin的null安全特性,您才能安全地使属性为null。如果某个东西不是一直可用,则应该可以为null。您滥用lateinit
的方式使您的属性变得不安全,迫使您记住检查它们是否已初始化,就像您在Java中总是必须记住检查字段是否为null一样。因此,您重新引入了Kotlin零安全解决的相同问题。
如果某些内容有时不可用,那么将其设为null是完全合适的。Kotlin的空安全功能将确保您不会忘记检查这种可能的状态。
更多阅读,来自Kotlin设计负责人:Null是你的朋友
CCD_ 11应该只由在CCD_ 12之后永远不能为null的属性使用。
----
也许你要问的是,如何更简洁地在Location上检查null,而不必处理其内部属性仍然可以为null?除了?.let
模式之外,您还可以使用以下内容:
val location = location ?: return
// you can now freely use non-null location in this function
// from the local reference known to be non-null
?.let
的情况与Java中的if (x != null)
模式非常相似,只是它对可能在多个线程中更改的属性也是安全的。
location?.let { location ->
// freely use location.getX() and getY() inside the braces
}
在这两种模式之间,代码总是和Java检查它的方法一样简洁
lateinit
只应用于保证变量初始化的情况,只是在编译时无法验证这种保证。一个很好的例子是Activity.onCreate
:虽然Kotlin编译器无法知道在onCreate
中初始化的字段在onResume
中保证不为null,但在Android SDK中这是一个相当强的契约,因此对该字段使用lateinit
并避免null检查是合理的。
对于在访问时不能保证为非null的任何内容,请坚持使用可为null的类型,并像往常一样进行null检查。对于任何保证为非null的内容,使用lateinit
而不进行任何额外检查:如果这些调用站点导致崩溃,请删除lateinit
并将类型更改为nullable。
您可以简化x?。让{}
更改:
location.let {
it.getX()
}
至:
location?.getX()
并设置一个默认值,如:
val x = location?.getX() ?: 0
我正在将一个项目从java迁移到kotlin,该项目使用了许多可以为null的变量,在与用户或外部作业进行一些交互之前,这些变量不会初始化。
您可以对需要等待交互的属性使用延迟初始化。
foo: MyType by lazy { ... }
有一篇关于使用"属性初始化"的帖子;"懒惰";与";lateinit";
正如Egor所指出的,lateinit应该在您可以保证属性将被初始化时使用。仍然可以使用检查初始化
this::myLateinitVar.isInitialized
但若您的源代码包含可为null的内容,那个么您应该明确地回到Kotlin中的可为null类型,并使用?操作人员毕竟,这是一个供你使用的工具。
答案涵盖了所有内容,但我只想解决这一点:"我更喜欢没有null安全性的旧java版本的项目,它更清晰、更简短">-这个问题在Java版本中并没有消失,只是被隐藏了。如果你不能保证这些属性是初始化的,那么无论你用什么语言写,如果你试图在它们准备好之前访问它们,你就有麻烦了!
如果可以保证它们将被初始化,请使用lateinit
。如果你不能,那么你可能想让它们为null;什么都没有";是一种潜在的状态,无论您在哪里访问它,都需要处理它
您可以使用this::myVariable.isInitialized
,但我觉得这更多的是一种情景设置,其中有一些早期设置必须处理潜在的临时未初始化状态,但在完成时,它肯定已经设置好了。如果主代码不能依赖于它的初始化,那么我觉得让它为null是更好的选择,因为这迫使你处理这种可能性,而且它更安全。如果你依赖isInitialized
,你可能会错过一个需要检查的点,因为它没有强制执行。
如果强制的null安全处理很烦人,请记住,Java中的情况也是一样的——这并不是在警告你所做的事情是危险的,你需要处理它!如果你确定一个东西不是null,你可以在它后面添加!!
来告诉null安全系统";别担心,相信我,我知道它不是空的;。在某些罕见的情况下,这是真的,而且你知道的比系统更多——但大多数人只使用它来禁用空安全系统,而忽略了问题,最终导致代码脆弱且可能损坏。它是来帮忙的!