覆盖 val 的行为的基本原理

发布于 2024-12-08 18:29:26 字数 581 浏览 1 评论 0原文

class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

打印:

A
B

但是,

class A { 
  lazy val x = println("A") 
}
class B extends A {
  override lazy val x = println("B")
}
(new B).x

仅打印:

B

根据Martin Odersky,至少在非惰性情况下,行为是“按照指定的”。我很好奇为什么以这种方式指定行为,以及为什么当 val 是惰性的时它会有所不同。

class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

Prints:

A
B

However,

class A { 
  lazy val x = println("A") 
}
class B extends A {
  override lazy val x = println("B")
}
(new B).x

Prints just:

B

According to Martin Odersky, the behaviour, at least in the non-lazy case, is "as specified". I'm curious as to why the behaviour is specified that way, and why it differs when the val is lazy.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

孤城病女 2024-12-15 18:29:26

类定义的模板(“主体”)中的代码(在成员定义之外)是进入构造函数的内容。当您初始化子类的实例时,始终会调用父类的构造函数(在本例中,您调用的是不带参数的构造函数,否则语法将为 class B extends A(arg1, arg2) { .. })。有关更多详细信息,请参阅 Scala 语言规范 中的第 5.1 节。

这就是为什么 println("A") 在第一种情况下被评估; val 定义是构造函数代码的一部分。

当您考虑第二个示例中构造时发生的情况时,您从未询问过 A 中定义的 x 的值;现在因为它是一个lazy val,所以在需要它之前不会被计算。

您可以将惰性 val 视为不带参数的方法,并在第一次调用时缓存其输出。您没有在这里调用该方法,您只是定义了它。

The code in the template ("body") of a class definition, outside of member definitions, is what goes into the constructor. Constructors for parent classes are always called when you initialize an instance of a child class (in this case, you are calling a constructor with no arguments, otherwise the syntax would be class B extends A(arg1, arg2) { ... }). For more details, see Section 5.1 in the Scala Language Specification.

That is why println("A") is evaluated in the first case; that val definition is part of the constructor code.

When you think about what happens at construction time in the second example, you never asked for the value of x defined in A; now because it is a lazy val, it will not be computed before it is needed.

You can think of lazy vals as methods that take no argument and that cache their output the first time they are called. You didn't call that method here, you just defined it.

望她远 2024-12-15 18:29:26
class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

在构造函数的执行过程中,会执行val(以及其他语句)的所有初始化。因此,val x = println("A") 作为 A 构造的一部分运行,并覆盖 val x = println("B") code> 作为 B 构造的一部分运行。它们都打印一些字符串并将 x 初始化为 () (类型为 Unit),因此覆盖在这里只是转移注意力。 .x(来自(new B).x)不执行任何操作。

在惰性情况下,您将 x 初始化为类似不带参数并返回 Unit 的函数。在您读取 .x 之前,它不会运行,并且由于 x 被覆盖,因此在这种情况下仅打印“B”。

class A { 
  val x = println("A") 
}
class B extends A {
  override val x = println("B")
}
(new B).x

During the execution of the constructor, all the initializations of vals (and other statements) are executed. Therefore, val x = println("A") is run as part of the construction of A and override val x = println("B") is run as part of the construction of B. They both print some string and initialize x to () (of type Unit), so override is just a red herring here. The .x (from (new B).x) does nothing.

In the lazy case, you're initializing x to something like a function taking no arguments and returning Unit. It is not run till you read .x, and, since x is overridden, only "B" gets printed in that case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文