在 Scala 中,父特征是否可以调用子 class 中实现的方法?
In Scala, is it possible for parent trait to call method implemented in child class?
我正在使用 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
正在使用 foo
方法,该方法在 Impl
class.
中实现
同样,Impl
class 扩展了 class A
(class Impl
的声明中的 with A
)。
这不是圆形吗?
怎么可能?
谢谢。
这里没有什么特别的,方法foo定义在trait中允许在impl中调用和实现它。从哪里调用它并不重要。
调用如下 -> 调用 fooA.它仅在 impl 继承的 A 中定义。 fooA 调用 foo。 foo 在特征中定义,实现出现在 impl 中。这不是循环而是最基本的用法。
如果有多个实现(例如在特征 A 中),则顺序将基于线性化(参见 )
从编译的角度来看,一切都通过了。 Base
要求扩展它的人为 foo
实现,这就是 Impl
所做的。特征 A
被允许使用 foo
因为它扩展了 Base
。一切都清楚了。
但是,我看到了你的困惑。不过,我不会真的称它为循环;它更像是在初始化(Impl
中)之前使用某些东西(A
中的foo
)。之所以有效,是因为 您使用了 def
。编译器知道这个值稍后可用(因为如果在编译过程的其余部分找不到它,它就会中断)并且它只是说 "I know it will be available at the point when it is invoked (given the compilation is successful), and it's a def
, which means I will be calculating it then and there. So I don't need to do it now".
但是,如果您使用 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。
我正在使用 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
正在使用 foo
方法,该方法在 Impl
class.
中实现
同样,Impl
class 扩展了 class A
(class Impl
的声明中的 with A
)。
这不是圆形吗?
怎么可能?
谢谢。
这里没有什么特别的,方法foo定义在trait中允许在impl中调用和实现它。从哪里调用它并不重要。
调用如下 -> 调用 fooA.它仅在 impl 继承的 A 中定义。 fooA 调用 foo。 foo 在特征中定义,实现出现在 impl 中。这不是循环而是最基本的用法。
如果有多个实现(例如在特征 A 中),则顺序将基于线性化(参见
从编译的角度来看,一切都通过了。 Base
要求扩展它的人为 foo
实现,这就是 Impl
所做的。特征 A
被允许使用 foo
因为它扩展了 Base
。一切都清楚了。
但是,我看到了你的困惑。不过,我不会真的称它为循环;它更像是在初始化(Impl
中)之前使用某些东西(A
中的foo
)。之所以有效,是因为 您使用了 def
。编译器知道这个值稍后可用(因为如果在编译过程的其余部分找不到它,它就会中断)并且它只是说 "I know it will be available at the point when it is invoked (given the compilation is successful), and it's a def
, which means I will be calculating it then and there. So I don't need to do it now".
但是,如果您使用 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。