5

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?

dk14
  • 22,206
  • 4
  • 51
  • 88
JavaMan
  • 351
  • 1
  • 3
  • 13
  • 1
    Because defs are not mutable varaibles, but method definitions. What do you want to happen if you do `age = 32` in SedanCar? – The Archetypal Paul Dec 23 '14 at 15:50
  • 5
    I think OP's question is totally reasonable. Scala is often presented as adhering to the uniform access principle, for good reason. It would not be unreasonable then to see the `age` var definition as nothing more than the definition of a pair of getter and setters (`age` and `age_=`) methods. In which case I should definitely be able to override `age`, which would actually override the getter only and leave the setter as is. – Régis Jean-Gilles Dec 23 '14 at 16:25

2 Answers2

12

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 :)

Community
  • 1
  • 1
dk14
  • 22,206
  • 4
  • 51
  • 88
  • 1
    I can buy the "what users expect" part, but I respectfully disagree that overriding only the getter would violate the UAP. After all, when doing so you still can read or write `age` by referencing `age` alone. I don't see how it could more of a "uniform notation". I can envision reasons for this limitation, but breaking the UAP is not one of them, quite the opposite. – Régis Jean-Gilles Dec 23 '14 at 23:59
  • As a side note I don't actually care much that one cannot override the getter alone. But in fact you cannot override a var at all and that one is a bummer. – Régis Jean-Gilles Dec 24 '14 at 00:09
  • 1) `var` is a uniform notation, `get` + `set` isn't. when you say "let's override only get!" - you're obviously preferring second and pushing user to think about getters/setters. 2) i think my example with formal type is representable enough to see why you can't override var "at all". – dk14 Dec 24 '14 at 03:52
  • 1*) in other words, if i want to forget about how my service is implemented - overriding a whole `var` will do the trick as it doesn't depend on what my service are: "computation" (i'm overriding both setters and getter methods, so don't have to think about them) or "storage" (variable is a naturally storage). Overriding only reader reminds me that it's actually implemented through computation (so it's method only). – dk14 Dec 24 '14 at 04:27
  • 2*) overriding a whole var with def makes it read-only, which definitely violates LSP: if `car.age = 42` doesn't work, `(car: Car).age = 42` also shouldn't - otherwise just accidental passing your `SeadanCar` to some function working with Car will make it mutable. – dk14 Dec 24 '14 at 04:28
  • "var is a uniform notation, get + set isn't": How so? Anyway, I could answer point by point, but I'll try to synthethize my point as such: say I define an `age`/`age_=` pair. I'll access `age` just like any "proper" var, and that's the point of the UAP. Now if I override `age` what I actually override is the getter. Everything is fine and the LSP is not broken. The spec says that `var x: T` is equivalent to `def x: T; def x_= (y: T ): Unit`, but when it comes to overriding, it's not. This is all I'm saying. Concerning the overriding of the *whole var*, your answer is spot on for sure. – Régis Jean-Gilles Dec 25 '14 at 09:23
  • i've been understanding your point about overriding `get` (and leaving `set` as is) from the begin :) but my point is: overriding only reader (as you said) reminds me that it's actually implemented through computation (so it's method only) even if you're not pushing setter to be private. You couldn't override only reader for service implemented as state (it's just a simple solid field). UAP means that user shouldn't think about reader or writer: "not betray whether they are implemented through storage or through computation" (wikipedia). So yes it's fine with LSP, but not with UAP – dk14 Dec 25 '14 at 09:30
  • 2
    Which is where I strongly disagree (the supposed relation with UAP), but I can get the argument in favor of treating a var as more than a getter and a setter (though inconvenient that may be in many practical cases). I am not even advocating that this design choice should be changed by the way, I am merely pointing that there is a genuinely interesting issue here and that OP's question was more valid (depending how you approach the issue) than many seemed to believe (especially given all the downvotes) – Régis Jean-Gilles Dec 25 '14 at 10:46
  • it's may be a little bit confusing because i've applied UAP to inheritance here (as it's also form of access). For example you said about buying the "what users expect" part :). So why user expects `def` overriding whole `var` instead of reader only? My point is, it's because user don't wanna know about how the `var` implemented: using storage or some readers/writers; he just want to work with `var` as `var` (which is natural). – dk14 Dec 25 '14 at 11:19
  • Unfortunatelly, scala's UAP implementation is inconsistent as you can: `trait Ttt { def a: Int = 7; def a_=(a: Int) = {}}; class Aaa2 extends Ttt { override def a: Int = 5}`. I'd solve this problem with special name for reader, like `def a_ =`, in that case you can work (inherit) `a` both as a state (using `override def a`) and as computation (using `override def a_; override def a_=`) – dk14 Dec 25 '14 at 11:19
  • 1
    And yes it's a good question - actually i'm one of 3 guys voted "+1". The only problem with the question was its initial formulation (upper case scala word etc) – dk14 Dec 25 '14 at 11:33
  • 1
    I get the reason why it is designed the way it is now. So much for UAP. While one should be able to do the overriding via @beanproperty annotation the default generated getters and setters doesn't for reasons well explained in your answers.`import scala.beans.BeanProperty class Car { @BeanProperty var age : Int = 30 } class SedanCar extends Car{ println ("age is "+super.getAge) override def getAge : Int = { 32 } }` – JavaMan Dec 26 '14 at 08:14
0

I think you have a problem with the concept. From Scala references:

A variable declaration var x: T is equivalent to declarations of a getter function x and a setter function x_=, defined as follows:

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

So you are trying to override a getter by "age = 54". But now you do not have any use for the 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.

nate-k
  • 191
  • 2
  • 10
  • 4
    Your answer precisely points to the core of the issue: `var x: T` is supposed to be equivalent to `def x: T; def x_= (y: T ): Unit`, but it's not. With the latter definition, I am totally entitled to override the getter only, leaving the setter as is. That's not true with the var definition. – Régis Jean-Gilles Dec 23 '14 at 16:23
  • 3
    Hahahahaha, yet another Scala specification bug. Neat! – gzm0 Dec 26 '14 at 08:44
  • @RégisJean-Gilles a declaration is not a definition. – som-snytt Jul 28 '15 at 14:51
  • True, but the distinction is hardly relevant here. And what is a declaration anyway, if it's not an **abstract** definition? You might technically consider "abstract definition" to be an oxymoron but this is a widely used and accepted, even by the scala spec itself. So pardon me if I just say "definition" when specifying whether it's concrete or abstract is irrelevant. – Régis Jean-Gilles Jul 28 '15 at 18:23