Kotlin 为什么我们应该避免使用基类的开放成员?

Kotlin 为什么我们应该避免使用基类的开放成员?,kotlin,Kotlin,当我阅读Kotlin时,我发现我们应该避免使用在基类中声明的open属性: 这意味着,在基类构造函数执行时,派生类中声明或重写的属性尚未初始化。如果在基类初始化逻辑中使用这些属性中的任何一个(直接或间接地,通过另一个重写的开放成员实现),则可能导致不正确的行为或运行时故障。因此,在设计基类时,应该避免在构造函数、属性初始值设定项和init块中使用开放成员 该文档称,当调用基类的构造函数时,派生类中的属性尚未初始化。但是,我们如何从基类构造函数访问未初始化的派生类属性(我假设错误行为或运行时故障是

当我阅读Kotlin时,我发现我们应该避免使用在基类中声明的
open
属性:

这意味着,在基类构造函数执行时,派生类中声明或重写的属性尚未初始化。如果在基类初始化逻辑中使用这些属性中的任何一个(直接或间接地,通过另一个重写的开放成员实现),则可能导致不正确的行为或运行时故障。因此,在设计基类时,应该避免在构造函数、属性初始值设定项和init块中使用开放成员


该文档称,当调用基类的构造函数时,派生类中的属性尚未初始化。但是,我们如何从基类构造函数访问未初始化的派生类属性(我假设
错误行为或运行时故障是由这种情况引起的)?有可能吗?

我不知道kotlin,但我假设
open
与其他语言中的
virtual
相同。在基类构造函数中调用虚拟成员是不安全的,因为基类构造函数是在派生构造函数之前调用的。如果重写属性要求完全初始化派生类,则可能会导致错误,因为当您在基构造函数中时,尚未调用派生构造函数。至少在C#之类的.NET语言中它是这样工作的。

Kotlin中的开放函数是可以被子类重写的函数。通常,限制类的继承是一种好的做法,因为您应该为类提供必要的代码,使其可重写。如果您的意图是不让一个类重写您的基类,那么您应该将其设置为final。因此,Kotlin通过使每个类和方法在默认情况下都是最终的,从而简化了这一过程。您可以在《Kotlin in Action》一书的对象和类一章中找到更详细的答案

当修改基类时,就会出现所谓的脆弱基类问题 可能导致子类的错误行为,因为基类的更改代码 更长时间匹配其子类中的假设。如果类没有提供精确的规则 它应该如何子类化(应该重写哪些方法以及如何重写), 客户端有可能以基类作者的方式重写方法 没想到。因为不可能分析所有的子类,所以基类是 “脆弱”是指任何变化都可能导致行为的意外变化 子类。 为了防止这个问题,Joshua Bloch(Addison Wesley, 2008),一本关于优秀Java编程风格的最著名的书,建议 您可以“为继承而设计和编写文档,或者禁止继承。”这意味着所有类和 不打算在子类中重写的方法需要 明确标记为最终的。 科特林遵循同样的哲学。而Java的类和方法是由 默认情况下,Kotlin的默认值为最终值


如果说“其他语言”,我会注意到至少C++ >代码>虚拟< /C>不存在这个问题:它是JVM和.NET语言。在JVM语言中,实例方法总是虚拟的。相反,在Kotlin中,
open
表示“非
final
”。(Java默认为not final,需要
final
关键字;但Kotlin正好相反。)-然而,这个答案的前提仍然成立:基类构造函数在(大多数)子类构造函数或任何初始化器之前调用。约书亚·布洛赫(Joshua Bloch)的优秀著作《有效Java》中的一项讨论了这个问题,该书指出,您应该为继承而设计和编写文档,或者禁止继承()。我认为科特林做出了一个很好的决定,将“最终”作为默认值。C#类在默认情况下是打开的,我认为这是一个设计错误。