为什么在Scala中用`def`覆盖`var`是不可能的?

时间:2014-12-23 15:12:43

标签: scala immutability liskov-substitution-principle mutability uap

虽然我理解为什么var无法覆盖子类中的val,反之亦然,但我无法理解为什么Scala不允许子类中的def覆盖{{超类中的1}}

var

由于class Car { var age = 32 } class SedanCar extends Car { override def age = 54 } 是可变的,为什么不允许var覆盖它?有人可以帮我理解这个吗?

2 个答案:

答案 0 :(得分:12)

这与Liskov Substitution Principle有关:您无法指定weaker access privileges in subclass (even for Java)。将var设为def会使设置者def x_= (y: T ): Unit变为私有(如@Staix所说)。因此,如果Seadan Car具有正式类型Car - 它不应被访问,但编译器一般不能找到这样的情况(只有正式类型在编译时已知),所以这样的行为是像任何较弱的特权一样禁用:

 val car2 = new SeadanCar

 car.age = 422 //compiler error, can't mutate "def age"

 val car: Car = new SeadanCar

 car.age = 42 //now you had mutated immutable

可替代性原则的要点是在投射到超类型后不应该改变行为。

另一方面,scala可以仅覆盖变量的getter部分,如@RégisJean-Gilles所说,但这不是那么明显的解决方案,因为直觉上用户希望varoverride def后变为不可变的。它实际上违反了Uniform Access Principle,因为你必须看到你的var作为两个服务(读者和作者)而不是真正的服务,而UAP兼容性要求相反:读者和作者都应该被代表一个统一的符号。

P.S。实际上你的问题指向scala的UAP兼容性不完整性。正如我所说的那样,只覆盖var的读者并保留编写器与UAP不一致 - 它阻止了覆盖var本身的能力(所以你不能在两个方面覆盖var:计算和类似存储),即使你已经因为LSP而无法覆盖它。但目前scala的解决方案也存在问题。你可能会发现:

 trait Car { def age: Int = 7; def age_=(a: Int) = {}}

 class SeadanCar extends Car { override def age: Int = 5}

但不能

 // just repeating your example

 trait Car { var age: Int = 7 } 

 class SeadanCar extends Car { override def age: Int = 5}

因此scala的继承似乎与UAP不兼容。恕我直言,最大的问题是读者和var本身具有相同的名称 - 所以你无法区分它们(定义时,不能访问)。我会用类似的东西解决它:

 trait Car { def age_: Int = 7; def age_=(a: Int) = {}}
 class SeadanCarReadOnly extends Car { override def age: Int = 5} //can't compile as reader is closed
 class SeadanCarVar extends Car { override var age: Int = 5} 
 class SeadanCarReadOnly extends Car { override def age_: Int = 5}

 trait Car2 { var age = 100500 }
 class SeadanCarReadOnly extends Car2 { override def age_: Int = 5}

请注意,在我提出的示例中覆盖age_应该导致:

scalaxx> (new SeadanCarReadOnly).age //call age_ here
resxx: Int = 5

scalaxx> (new SeadanCarReadOnly).age_
resxx: Int = 5

不喜欢:

 trait Car2 { @BeanProperty var age = 100500 }
 class SeadanCarReadOnly extends Car2 { override def getAge: Int = 5}

 //which leads to inconsistency:

 scala> (new SedanCar()).age
 res6: Int = 30

 scala> (new SedanCar()).getAge
 res7: Int = 54

对于cource,此类方法应同时禁用覆盖var agedef age_; def age_=

 trait Car2 { var age = 100500 }
 class SeadanCarReadOnly extends Car2 { 
    override var age = 17; 
    override def age_: Int = 5 //should be compile error here
 }

但由于向后兼容性,很难在Scala语言中快速实现

P.S./2请注意,关于问题的可变性/不可变性部分,你定义不能这样做(由于LSP):

 trait Car { var age: Int = 32 } //or without initial value
 class SedanCar extends Car { override val age = 42 }

并且,由于LSP + UAP,不应该这样做:

 trait Car { def age: Int = 7; def age_=(a: Int) = {}}
 class SedanCar extends Car { override val age = 42 }

无论您可以:)

答案 1 :(得分:0)

我认为你对这个概念有疑问。来自Scala参考:

变量声明var x:T等同于a的声明 吸气功能x 和setter函数x_ =,定义如下:

def x: T
def x_= (y: T ): Unit

所以你试图用“年龄= 54”覆盖一个吸气剂。但是现在你对setter没有任何用处。

希望你明白我的意思。我认为为什么人们“减去”你的问题是因为你不是在思考Scala的思维方式。