Scala 用值重写抽象方法是一种好的做法吗?
在Scala中,字段和方法属于同一名称空间,因此字段可以轻松重写方法。这是一个非常吸引人的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 {
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
。这能回答问题吗?