简体   繁体   English

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

[英]Why it's impossible to override `var` with `def` in Scala?

While I understand why a var cannot override a val in subclass and vice versa, I am unable to understand why does Scala not allow a def in subclass to override a var in superclass 虽然我理解为什么var不能覆盖子类中的val ,反之亦然,但我无法理解为什么Scala不允许子类中的def覆盖超类中的var

class Car {
  var age = 32
}

class SedanCar extends Car {
  override def age = 54
}

As var is mutable why not allow a def to override it? 因为var是可变的,为什么不允许def覆盖呢? Can anyone please help me in understanding this? 有人可以帮我理解这个吗?

That's related to the Liskov Substitution Principle : you can't assign weaker access privileges in subclass (even for Java) . 这与Liskov替换原则有关 :您不能在子类中分配较弱的访问权限(即使对于Java) Making var a def makes the setter def x_= (y: T ): Unit private (as @Staix said). 使vardef使得setter def x_= (y: T ): Unit private(如@Staix所说)。 So in case when Seadan Car has formal type Car - it should not be accessed, but compiler can't find such cases in general (only formal types are known in compile-time), so such behavior is disabled like any weaker privilege: 因此,如果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

The point of Substitutability Principle is behavior should not be changed after casting to the supertype. 可替代性原则的要点是在转换为超类型后不应改变行为。

On the other hand, scala could override only getter part of variable as @Régis Jean-Gilles said, but this is not so obvious solution, because intuitively user expects a var becoming immutable after override def . 另一方面,scala可以仅覆盖变量的getter部分,如@RégisJean-Gilles所说,但这并不是那么明显的解决方案,因为直观地用户期望varoverride def后变为不可变。 And it's actually violating Uniform Access Principle as you have to see your var as two services (reader and writer) instead of one it really is, while UAP-compatibilty requires the opposite: both reader and writer should be represented by one uniform notation. 它实际上违反了统一访问原则,因为你必须将你的var视为两个服务(读者和作者)而不是它实际上的一个,而UAP兼容性要求相反:读者和作者都应该用一个统一的符号来表示。

PS Actually your question points to scala's UAP-compatibility incompleteness. PS其实你的问题指向scala的UAP兼容性不完整性。 As i said, overriding only var 's reader and leaving writer as is will be inconsistent with UAP - it's blocking ability to override var itself (so you can't override var in both ways: computational and storage-like), even regardless that you already can't override it due to LSP. 正如我所说的那样,只覆盖var的读者并保留编写器与UAP不一致 - 它阻止var本身的阻塞能力(所以你不能在两种方式中覆盖var:计算和存储一样),即使不管怎样由于LSP,你已经无法覆盖它了。 But current scala's solution is also problematic. 但目前scala的解决方案也存在问题。 You may find that you can: 你可能会发现你可以:

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

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

But can't 但不能

 // just repeating your example

 trait Car { var age: Int = 7 } 

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

So scala's inheritance seems not to be compatible with UAP. 因此scala的继承似乎与UAP不兼容。 IMHO, the big problem is that reader and var itself have identical names - so you can't distinguish them (when defining, not accessing). 恕我直言,最大的问题是读者和var本身具有相同的名称 - 所以你无法区分它们(定义时,不能访问)。 I'd solve it with something like: 我会用以下的东西解决它:

 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}

Note, that overriding age_ in my proposed example should lead to: 请注意,在我提出的示例中,覆盖age_应该导致:

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

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

Not like: 不喜欢:

 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

Of cource, such approach should disable overriding var age and def age_; def age_= 对于cource,这种方法应该禁用覆盖var agedef age_; def age_= def age_; def age_= simultaneously: 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
 }

but this is hard to quickly implement it in Scala language due to backward compatibility 但由于向后兼容性,很难在Scala语言中快速实现它

PS/2 Just to mention, regarding mutability/immutabilty part of the question, you definetely can't do this (due to LSP): PS / 2只是提一下,关于问题的可变性/不可变性部分,你定义不能这样做(由于LSP):

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

And, due to LSP + UAP, shouldn't be able to do this: 并且,由于LSP + UAP,不应该这样做:

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

regardless the fact that you can :) 不管你可以:)

I think you have a problem with the concept. 我认为你对这个概念有疑问。 From Scala references: 来自Scala参考:

A variable declaration var x: T is equivalent to declarations of a getter function x and a setter function x_=, defined as follows: 变量声明var x:T等同于getter函数x和setter函数x_ =的声明,定义如下:

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

So you are trying to override a getter by "age = 54". 所以你试图用“年龄= 54”来覆盖一个吸气剂。 But now you do not have any use for the setter. 但是现在你对setter没有任何用处。

Hope you get what I mean. 希望你明白我的意思。 I think why people "minus" your question is because you are not thinking in the mindset of Scala here. 我认为为什么人们“减去”你的问题是因为你不是在思考Scala的思维方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM