我想知道如果两个类彼此子类化将会发生什么,并且我使用IntelliJ IDEA使用Scala尝试了它。实现没有错误,但是当我取消注释test
对象内的行时,IDE就会冻结,我无能为力。这里真的发生了什么?
class Shoe extends Table {
}
class Table extends Shoe {
}
object test {
// val shoe = new Shoe()
}
答案 0 :(得分:2)
似乎您的IDE版本正在尝试解析代码的类结构以及早识别错误和/或提供范围内的自动填充建议。但它似乎没有条件停止跟踪/识别递归子类。
确保更新到最新版本,如果没有解决此问题,请提交错误报告。在此之前,可能有一个设置可以在IDE中禁用,因此它不会冻结尝试解决循环类依赖关系。更改自动完成设置时,更正和建议可能会失去一些准确性或完全禁用,但它允许您运行/编译代码以识别错误(循环类依赖)并修复它。
循环类依赖是指多个类在循环中从彼此继承。例如,如果我们有两个类class A
和class B
,我们可以创建一个循环依赖循环,如下面的伪代码所示:
class A inherits B;
class B inherits A;
当许多编译器或解释器对此进行评估时,他们会看到第一行并尝试加载B
。要加载B
,他们会转到第2行的声明。然后他们会看到需要加载A
。在这里,智能编译器或解释器将翻转表,因为它已经知道它已经尝试并且无法加载A
。一个天真的人会很乐意遵循指示。
load B!
load A!
load B!
load A!
load B!
load A!
最聪明的编译器和解释器实际上可以加载循环类依赖项并创建有效的代码。但这些非常罕见。作为程序员,有时在没有这种能力的语言中拥有循环类依赖是绝对必要的。
这就是类组合的概念所在。您可以以变量的形式将B
的工作副本提供给类A
,而不是继承超类。
在某些情况下,另一个很好的选择是提供转换功能,允许B
成为类A
;如果它们都是从彼此继承的话,尽管它们的行为不同,它们很可能包含相同的数据!某些语言具有执行此转换的内置功能。最简单的例子是“类型”'。有些语言也将这种转换概括为参数多态,您可以编写一个可以在任何兼容类型上运行的函数,而无需先进行转换。
引入一个难以解析的代码总是一种很好的方法来捕获编译器和解释器中的错误。因此,当您必须编写并测试一个时,请记住这个片段!
在某些语言中,像这样的东西,而不是一个bug,被认为是未定义的行为。你不想在你依赖的任何代码中弄乱未定义的行为。
答案 1 :(得分:1)
没什么,编译器拒绝这段代码:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Shoe extends Table {}
class Table extends Shoe {}
// Exiting paste mode, now interpreting.
<pastie>:15: error: illegal cyclic reference involving class Shoe
class Table extends Shoe {
^