如何处理未初始化的lateinit变量



我正在将一个项目从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安全系统";别担心,相信我,我知道它不是空的;。在某些罕见的情况下,这是真的,而且你知道的比系统更多——但大多数人只使用它来禁用空安全系统,而忽略了问题,最终导致代码脆弱且可能损坏。它是来帮忙的!

最新更新