Scala 用值重写抽象方法是一种好的做法吗?

Scala 用值重写抽象方法是一种好的做法吗?,scala,Scala,在Scala中,字段和方法属于同一名称空间,因此字段可以轻松重写方法。这是一个非常吸引人的Scala特性。例如,它允许在类层次结构的某个点将类方法固定到特定引用: abstract class AC { def x: Int } class C extends AC { override val x = 10 } 假设有第二个Base的属性计算形式x: abstract class AC { def x: Int; val y: Int = x*2 } class C extends AC {

在Scala中,字段和方法属于同一名称空间,因此字段可以轻松重写方法。这是一个非常吸引人的Scala特性。例如,它允许在类层次结构的某个点将类方法固定到特定引用:

abstract class AC { def x: Int }
class C extends AC { override val x = 10 }
假设有第二个
Base
的属性计算形式
x

abstract class AC { def x: Int; val y: Int = x*2 }
class C extends AC { override val x = 10 }

val v = (new C).y
人们可以期望
v
值为
20
,但它是
0
好的,现在发生的事情是,
AC
构造函数在
C
之前被调用,因此,
x
(现在是一个属性)在那一点上还没有设置,
构造函数使用整数的默认值(我可能错了)

让我印象深刻的是:

class C extends AC { override def x = 10 }

val v = (new C).y
v
等于20

我有一种预感,这种差异来自于这样一个事实,即方法以不同于值的方式得到解决。有人能解释一下这种行为差异吗?有关Scala构造函数如何工作的更多详细信息? 在这两种情况下有相同的行为不是更连贯吗


关于问题的标题:将方法重写为值不是很危险吗?一些客户端代码可以对库类执行此操作,而不会注意到从属属性可能会获得无效值,从而生成错误。

超类的字段在子类的字段之前实例化。如此给定

abstract class AC {
  def x: Int
  val y: Int = x * 2
}

class C extends AC {
  override val x = 10
}
newc
将首先实例化
AC
的字段,在本例中是
y
,然后是
C
的字段,即
x
。由于初始化之前
Int
值是
0
,因此
y=x*2
将是
0

现在,如果将
x
重写为一个方法(
def
),则
C
没有要初始化的字段,并且
y=x*2
调用方法
x
,该方法返回
10
,因此
y
取值
20
Update:为了清楚起见,方法不属于单个实例,而是在同一类的所有实例之间共享。因此,方法
x
是在编译时创建的,而不是在创建
C
的实例时创建的

<>我认为危险的是<代码> y <代码> >代码> AC/<代码>是<代码> Valu/代码>;这样做会强制在初始化子类的字段之前实例化
y
,形成潜在的正向引用。如果不想将
y
实现为方法,可以将其实现为
lazy val
,这意味着仅在对象完全实例化后,才对其第一次使用时进行一次评估

abstract class AC {
  def x: Int
  lazy val y: Int = x * 2
}

class C extends AC {
  override val x = 10
}

println((new C).y) // prints 20, because y is initialized on its first use
顺便说一下:如果将
val
重写为
val
,则会出现相同的问题

class AB {
  val x = 10
  val y = x * 2
}
class B extends AB {
  override val x = 4
}

println((new B).y) // prints 0, because the overridden `x` is not yet initialized

我认为更好的做法是使
y
变懒,或者在引用可能被覆盖的内容时使用def。虽然我不知道为什么第二次初始化有问题。在规范中很难找到关于如何使用val覆盖def的任何内容。如我在问题中所述,当
x
是一个属性时,我希望
y
为0;令我惊讶的是,当
x
是一种方法时,
y
不是0。当
AC
的构造函数实例化
y
时,我可以从您的回答中理解,调用
x
方法是子类提供的实现。您的编辑告诉我,我可能是表达错误:当然,方法不属于单个实例,这不是这里的问题。就我目前所知,当
x
是一种方法时,
y
获得正确值的事实可以解释为
AC
构造函数调用
x
@PabloFranciscoPérezHidalgo是的
x
方法的
C
(类)实现,根据运行时类型选择正确的实现,这里它是
C
的一个实例。虽然这个例子是C++的,但是维基百科页面上有一个可能的内存布局的例子。我认为Scala的总体想法也是如此。还请注意,Scala为每个
val
创建了一个getter方法,从而可以将
def
重写为
val
。这能回答问题吗?