[英]Why it's impossible to override `var` with `def` in Scala?
虽然我理解为什么var
不能覆盖子类中的val
,反之亦然,但我无法理解为什么Scala不允许子类中的def
覆盖超类中的var
class Car {
var age = 32
}
class SedanCar extends Car {
override def age = 54
}
因为var
是可变的,为什么不允许def
覆盖呢? 有人可以帮我理解这个吗?
这与Liskov替换原则有关 :您不能在子类中分配较弱的访问权限(即使对于Java) 。 使var
为def
使得setter def x_= (y: T ): Unit
private(如@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所说,但这并不是那么明显的解决方案,因为直观地用户期望var
在override def
后变为不可变。 它实际上违反了统一访问原则,因为你必须将你的var
视为两个服务(读者和作者)而不是它实际上的一个,而UAP兼容性要求相反:读者和作者都应该用一个统一的符号来表示。
PS其实你的问题指向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 age
和def age_; def age_=
def 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语言中快速实现它
PS / 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 }
不管你可以:)
我认为你对这个概念有疑问。 来自Scala参考:
变量声明var x:T等同于getter函数x和setter函数x_ =的声明,定义如下:
def x: T
def x_= (y: T ): Unit
所以你试图用“年龄= 54”来覆盖一个吸气剂。 但是现在你对setter没有任何用处。
希望你明白我的意思。 我认为为什么人们“减去”你的问题是因为你不是在思考Scala的思维方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.