简体   繁体   English

在Scala中,父特征是否可以调用在子类中实现的方法?

[英]In Scala, is it possible for parent trait to call method implemented in child class?

I am using Scala, and wondering why this code works. 我正在使用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)

The result of a.fooA(10) is 10. a.fooA(10)的结果是10。
However, in trait A , method fooA is using foo method which is implemented in Impl class. 但是,在特征A ,方法fooA使用的是在Impl类中实现的foo方法。
Again, Impl class extends class A ( with A in the declaration of class Impl ). 同样, Impl类扩展了类A (在类Impl的声明中with A )。

Isn't it circular? 它不是圆形的吗?
How is it possible? 这怎么可能?

Thanks. 谢谢。

There is nothing special here, the method foo is defined in the trait allowing it to be called and implemented in impl. 这里没有什么特别的,方法foo是在trait中定义的,允许在impl中调用和实现它。 It isn't really important where it is called from. 从何处调用它并不重要。

The calling goes as follows -> call fooA. 调用如下->调用fooA。 It is only defined in A which impl inherits. 它仅在imp继承的A中定义。 fooA calls foo. fooA呼叫foo。 foo is defined in the trait and the implementation appears in impl. foo在trait中定义,实现在impl中出现。 This is not circular but rather the most basic usage. 这不是循环的,而是最基本的用法。

If there were more than one implementation (eg in trait A) then the order would be based on linearization (see https://stackoverflow.com/a/34243727/1547734 ) 如果有多个实现(例如,在特征A中),则该顺序将基于线性化(请参阅https://stackoverflow.com/a/34243727/1547734

From the compilation point of view, everything checks out. 从编译的角度来看,一切都可以检出。 Base demands an implementation for foo by whoever extends it, and this is what Impl does. Base要求任何人扩展foo的实现,而Impl就是这样做的。 Trait A is allowed to use foo because it extends Base . 特质A被允许使用foo因为它扩展了Base Everything is clear. 一切都清楚了。

However, I see your confusion. 但是,我看到您的困惑。 I wouldn't really call it circular though; 我不会真的称其为循环。 it's more like using something ( foo in A ) before it has been initalized (in Impl ). 它更像是在Impl之前(在Impl )使用某种东西( A foo )。 Reason this works is because you used def . 之所以可行,是因为您使用了def Compiler knows that this value will be available later (because if it doesn't find it in the rest of the compilation process, it will break) and it just says "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". 编译器知道此值以后会可用(因为如果在其余的编译过程中找不到该值,它将中断),并且只是说“我知道在调用该值时该值将可用(给定)编译成功),并且它是一个def ,这意味着我将在那儿进行计算。因此,我现在不需要这样做。”

If you use val , however, foo will be initialized at that point in A so you will get that initial value, which is 0 for Interegers: 但是,如果使用val ,则fooA那一点被初始化因此您将获得该初始值,对于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

Note that lazy val has the same effect as def (it's also calculated lazy, it's just that it will be calculated only once, at the point of first usage), so modifying the above code to override lazy val foo: Int = 42 would result in printing 42 too. 请注意, lazy valdef具有相同的效果(它也是lazy计算的,只是在第一次使用时它只会被计算一次),因此修改上面的代码以override lazy val foo: Int = 42将导致在打印42。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM