5

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

class Car {
  var age = 32
}

class SedanCar extends Car {
  override def age = 54
}

既然var是可变的,为什么不允许 adef覆盖它?谁能帮助我理解这一点?

4

2 回答 2

12

这与Liskov 替换原则有关:您不能在子类中分配较弱的访问权限(即使对于 Java)。制作varadef使 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 部分,但这并不是那么明显的解决方案,因为用户直观地期望varoverride 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 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 语言快速实现它

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 }

不管你可以:)

于 2014-12-23T17:05:51.517 回答
0

我认为你的概念有问题。来自 Scala 参考资料:

变量声明 var x: T 等价于 getter 函数 x 和 setter 函数 x_= 的声明,定义如下:

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

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

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

于 2014-12-23T16:03:28.150 回答