我正在使用Scala,并想知道为什么这段代码有效。
trait Base {
def foo(x: Int): Int
}
trait A extends Base {
def fooA(x: Int): Int = {
foo(x)
}
}
class Impl extends Base with A {
override def foo(x: Int): Int = x
}
val a = new Impl
a.fooA(10)
a.fooA(10)
的结果是 10。
但是,在特征A
中,方法fooA
正在使用Impl
类中实现foo
方法。
同样,Impl
类扩展了类A
(类Impl
的声明中with A
)。
这不是循环的吗?
怎么可能?
谢谢。
从编译的角度来看,一切都检查出来了。Base
要求无论谁扩展它,它都foo
实现,这就是Impl
所做的。允许特质A
使用foo
,因为它扩展了Base
。一切都清楚了。
但是,我看到你的困惑。不过,我不会真正称它为循环;它更像是在启动之前使用某些东西(foo
在A
中),在它被启动之前(在Impl
中)。这样做的原因是因为您使用了def
.编译器知道这个值稍后会可用(因为如果在编译过程的其余部分找不到它,它就会中断),它只是说"我知道它在调用时可用(假设编译成功),它是一个def
,这意味着我将在那里计算它。所以我现在不需要这样做"。
但是,如果您使用val
,foo
将在A
的那个点初始化,因此您将获得该初始值,对于 Interegers,该值为 0:
trait Base {
val foo: Int
}
trait A extends Base {
val fooA: Int = foo
}
class Impl extends Base with A {
override val foo: Int = 42
}
val a = new Impl
println(a.fooA) // prints 0 although we wanted 42
请注意,lazy val
与def
具有相同的效果(它也是惰性的计算,只是在第一次使用时只会计算一次),因此将上述代码修改为override lazy val foo: Int = 42
也会导致打印 42。
这里没有什么特别的,方法foo是在trait中定义的,允许它在impl中调用和实现。从哪里调用它并不重要。
调用如下 ->调用 fooA。它仅在 A 中定义,它包含继承。fooA叫foo。Foo 在 trait 中定义,实现出现在 impl 中。这不是循环,而是最基本的用法。
如果有多个实现(例如在特征A中),则顺序将基于线性化(见 https://stackoverflow.com/a/34243727/1547734)