19

Why it is not possible to override mutable variable in scala ?

class Abs(var name: String){
}

class AbsImpl(override var name: String) extends Abs(name){
}

Above code gives following compile time error :-

variable name cannot override a mutable variable

If name is declared val, then above code works fine.

mogli
  • 1,549
  • 4
  • 29
  • 57
  • Why would someone want to `override` a `var` ? What's the meaning of overriding a var ? – sarveshseri Jul 14 '15 at 05:35
  • 1
    Because overriding it doesn't really make a lot of sense. It's mutable, you can just change it. What does overriding even mean? http://stackoverflow.com/questions/16413986/how-to-override-a-mutable-variable-in-trait-in-scala – Falmarri Jul 14 '15 at 05:38
  • @Sarvesh : Thanks for your quick response. I am just trying to understand, why it is allowed for val ? – mogli Jul 14 '15 at 05:39
  • Because the val has a single fixed value. Which you can change when you override it. – The Archetypal Paul Jul 14 '15 at 07:07
  • @Falmarri because you may want an argument of the constructor to set the value of this variable, and override would let this argument has the exact same name as this variable since it would be this very variable. As of now, you need to come up with a new name for this argument. I use myvar_ with a trailing underscore as a convention for an argument that will set a var of the same name, minus the underscore, and should never be used again. – Profiterole Mar 18 '21 at 18:06

5 Answers5

7

The short answer: you need to pass -Yoverride-vars to the Scala compiler.

According to the spec, a var is both a getter and a setter, and normal overriding rules apply for these methods. However, this proved to have some unwanted consequences w.r.t. to the final keyword and inlining. The code in the compiler mentions some spec clarifications would be needed:

// TODO: this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here.
if (!settings.overrideVars)
  overrideError("cannot override a mutable variable")

A related ticket: SI-3770

Iulian Dragos
  • 5,692
  • 23
  • 31
5

If you could override a var with a var, then the overriding member could have a narrower type. (That's how overriding is defined.)

You could then assign a value of a wider type and then read it expecting the narrower type, and fail.

Illustration of the setter involved:

scala> class A ; class B extends A
defined class A
defined class B

scala> abstract class C { var x: A } ; class D extends C { var x: B = _ }
<console>:13: error: class D needs to be abstract, since variable x in class C of type A is not defined
(Note that an abstract var requires a setter in addition to the getter)
       abstract class C { var x: A } ; class D extends C { var x: B = _ }
                                             ^

scala> abstract class C { var x: A }
defined class C

scala> class D extends C { var x: B = _ ; def x_=(a: A) = ??? }
defined class D
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • When you override, it's not that you can have a "narrower type". Actually, it overrides two methods, the getter and the setter. The first one is covariant in the var's type, the second is contravariant in the var's type, so even if you were to override, the type would be invariant. – Mikaël Mayer Mar 15 '16 at 10:44
  • @MikaëlMayer That's another way of saying what I demonstrated, except that the definition of overriding member begins with conformance and not with desugaring. http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#overriding – som-snytt Mar 15 '16 at 15:18
  • The interpretation of vars as getter and setter is the only one that is backed by the spec. The conformance relation doesn't treat variables, only methods and vals, but variables are defined to be "equivalent" to a getter and setter. http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#variable-declarations-and-definitions. I would say it should be allowed. Instead it's disallowed even if one tries to override via a getter and setter. The mystery continues. – Iulian Dragos Jan 25 '17 at 11:34
4

I believe that the intent was simply to set the value of the inherited var name. This can be achieved this way (without override var):

class Abs(var name: String){
}

class AbsImpl(name: String) extends Abs(name){
}

The ambiguity arises from the local var name: String in AbsImpl that is named after the inherited var name: String from Abs. A similar code, less syntactically ambiguous but also less elegant would be:

class Abs(var name: String){
}

class AbsImpl(name_value: String) extends Abs(name_value){
}
Profiterole
  • 167
  • 1
  • 4
2

When you want to overriding a var its equivalent to trying to override a field in java which is not possible.

Robin
  • 6,879
  • 7
  • 37
  • 35
1

This happens when the var you are trying to override already has an assignment. I am not sure why this is forbidden, but also it makes little sense.

See also this question.

Define name as abstract instead

trait Abs {
  var name: String
}

class AbsImpl(name0: String) extends Abs {
  var name = name0
}

or

trait Abs {
  var name: String
}

class AbsImpl(private var name0: String) extends Abs {
  def name = {
    println("getter")
    name0
  }

  def name_=(value: String) = {
    println("setter")
    name0 = value
  }
}
0__
  • 66,707
  • 21
  • 171
  • 266