Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
320 views
in Technique[技术] by (71.8m points)

immutability - 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

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? Can anyone please help me in understanding this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

That's related to the Liskov Substitution Principle: you can't assign weaker access privileges in subclass (even for Java). Making var a def makes the setter def x_= (y: T ): Unit private (as @Staix said). 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:

 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. 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.

P.S. Actually your question points to scala's UAP-compatibility incompleteness. 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. But current scala's solution is also problematic. 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. IMHO, the big problem is that reader and var itself have identical names - so you can't distinguish them (when defining, not accessing). 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:

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_= simultaneously:

 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

P.S./2 Just to mention, regarding mutability/immutabilty part of the question, you definetely can't do this (due to 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:

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

regardless the fact that you can :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...