虽然我理解为什么 avar
不能覆盖val
子类中的 a ,反之亦然,但我无法理解为什么 Scala 不允许def
子类中的 a 覆盖var
超类中的 a
class Car {
var age = 32
}
class SedanCar extends Car {
override def age = 54
}
既然var
是可变的,为什么不允许 adef
覆盖它?谁能帮助我理解这一点?
虽然我理解为什么 avar
不能覆盖val
子类中的 a ,反之亦然,但我无法理解为什么 Scala 不允许def
子类中的 a 覆盖var
超类中的 a
class Car {
var age = 32
}
class SedanCar extends Car {
override def age = 54
}
既然var
是可变的,为什么不允许 adef
覆盖它?谁能帮助我理解这一点?
这与Liskov 替换原则有关:您不能在子类中分配较弱的访问权限(即使对于 Java)。制作var
adef
使 setter 成为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
可替换性原则的要点是在转换为超类型后不应更改行为。
另一方面,正如@Régis Jean-Gilles 所说,scala 只能覆盖变量的 getter 部分,但这并不是那么明显的解决方案,因为用户直观地期望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不兼容。恕我直言,最大的问题是 reader 和 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
当然,这种方法应该禁用覆盖var 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
所以你试图用“age = 54”覆盖一个吸气剂。但是现在您对 setter 没有任何用处。
希望你明白我的意思。我认为为什么人们“减去”您的问题是因为您在这里没有考虑 Scala 的思维方式。