6

我正在学习 Kotlin,读到这个lateinit关键字让我怀疑它的用处。考虑这段代码:

var testString: String? = null

lateinit var lateTestString: String

fun print() {
    print(testString?.length)

    print(lateTestString.length)
}

这里获取字符串长度的唯一区别是使用?.运算符检查它是否为空。在访问属性或调用方法时是否使用了lateinit不必添加额外问号的快捷方式?就这一事实而言,我认为添加额外的问号比访问异常时更值得lateinit

更多的研究表明,这lateinit对于变量尚未初始化的注入和/或单元测试很有用,但它会。但是,难道不值得拥有额外的东西?.而不是.不冒例外的风险吗?

4

4 回答 4

16

lateinit关键字的存在是为了启用一种特定的场景:当您的字段不能为空,但您也不能在构造函数中或使用常量值初始化它时。您必须确保在使用该值之前对其进行初始化。如果你不这样做,你会得到一个意义明确的特殊例外。

lateinit 用法和“正常”可空字段之间的区别在于?.后者传达了关于代码的错误信息:“这个东西有时可以为空”。当,事实上,它不能。它只是比平常晚初始化(例如,使用依赖注入而不是构造函数)。

于 2018-01-10T13:20:07.343 回答
4

正在使用lateinit不必添加额外问号的快捷方式

实际上它更接近于!!. 我在我的代码中经常使用它,原因我将尝试描述。

故意选择这两个!!是为了吸引人们注意代码中“与类型系统打赌”的地方,可以这么说。当在适当的地方使用时,这正是您想要的,但是实际项目中所有那些实际上是non-null的 var 呢,只是类型系统太弱而无法证明呢?!!当我可以很容易地确定它们已被初始化时,我不想看到我的代码库周围的一切都在扩散。这种噪音会削弱!!发送的强信号。

当您lateinit var在代码中看到 a 时,您知道您只需查找周围上下文指定的任何初始化方法,以说服自己一切都很好。检查它是否正确使用非常容易,而且我从未见过由此产生的错误。

实际上,很高兴看到 Kotlin 设计者将现实开发人员的关注点置于严格的类型形式之上。

于 2018-01-10T13:26:22.450 回答
4

我不是来自 Jetbrains 团队,所以也许我在这里没有清晰的画面,但我同意你的看法,这lateinit看起来不是一个好的结构。

添加时的最初想法lateinit是我们有一些框架(提示:Android),有时框架的用户无法访问类的构造函数(提示:Android 的 Activity 类),因为他无法初始化一些属性构造函数或init块。但是,由于这些类具有某种生命周期,我们可以确定属性foo将在第一次使用之前被初始化,因为例如初始化发生在 中onCreate(),而属性在 中使用onResume(),稍后会发生。

(在过去的某个地方,L - 懒惰的程序员,J - Jetbrains):

L:嘿,Jetbrains!我们很懒,如果我们确定属性将被初始化,我们不想要那个额外的问号。我们能否以某种方式标记它以克服 Kotlin 的空安全性?

J:是的,当然!让我们拍一下lateinit修饰符。

好主意?

不。

因为在该语言的更高版本中,它的创建者决定为lateinit属性添加新语法。我可能是错的(没有太注意它),但它看起来像foo::isInitialized. 原因是这个修饰符被滥用了(或者它可能从一开始就有缺陷?),所以我们需要对其进行一些额外的检查。

所以,基本上,我们用问号和整个空安全性来换取执行foo::isInitialized检查以防止UninitializedPropertyAccessException(或其他东西)的绝佳机会。

于 2018-01-10T13:26:12.353 回答
0

lateint 意味着稍后
在此代码中初始化您会收到错误。您必须在调用 lateTestString 之前对其进行初始化。

var testString: String? = null

lateinit var lateTestString: String

fun print() {
    print(testString?.length)

    print(lateTestString.length)
}
于 2018-01-10T13:11:27.520 回答