[英]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). 使var
为def
使得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所说,但这并不是那么明显的解决方案,因为直观地用户期望var
在override 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 age
和def 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.