tags:

views:

174

answers:

4

I'm wanting to do something like this:

class A (var updateCount: Int) {
}

class B (val name: String, var updateCount: Int) extends A(updateCount) {
  def inc(): Unit = {
    updateCount = updateCount + 1
  }
}

var b = new B("a", 10)
println(b.name)
println(b.updateCount)

b.updateCount = 9999
b.inc
println(b.updateCount)

but the compiler doesn't like it.

(fragment of extend.scala):5: error: error overriding variable updateCount in class A of type Int;
 variable updateCount needs `override' modifier
class B (val name: String, var updateCount: Int) extends A(updateCount) {

Adding override on updateCount doesn't work either. What's the clean way to do this?

+4  A: 

You don't need to declare the var in the subclass constructor signature:

class B (val name: String, /* note no var */ updateCount: Int) extends A(updateCount) {
  //...
}

This also extends to classes with vals in their constructors also:

scala> class C(val i: Int)
defined class C

scala> class D(j: Int) extends C(j)
defined class D
oxbow_lakes
This doesn't work. If I use the exact example I provided, removing the var from updateCount, I get(fragment of extend.scala):7: error: reassignment to val updateCount = updateCount + 1coming out of the inc method.
trenton
+2  A: 

You need to avoid having the identifier declared for B shadowing the identifier declared for A. The easiest way to do that is to chose a different name. If, however, you really, really don't want to do that, here is an alternative:

class B (val name: String, updateCount: Int) extends A(updateCount) {
  self: A =>
  def inc(): Unit = {
    self.updateCount = self.updateCount + 1
  }
}

And, by the way, drop the var from B declaration.

Daniel
Wow, that's quite a work around. This seems like a strange limitation of the language. If I had a inheritance graph 5 deep, would I need to continue to come up with "fake" names for these args? updateCountDontUse1, and in its subclass updateCountDontUse2, etc? It's not just the name... it's the cruft, too. It's an extra field that's sitting in my class, begging someone to use it and get bad results.
trenton
That's the thing... the parameters to the class are not really fields, unless preceeded with `val` or `var`. If you do not use them anywhere but the constructor, they won't be stored as fields in the class.
Daniel
A: 

When you use the var keyword in the parameters, this makes the value passed in be a new var in the class. When you don't do that, it makes it a constructor parameter (only), and you use it's value for the var by passing it to the constructor. So the example that people have given

class B(val name:String, updateCount:Int) extends A(updateCount)

is correct.

However, scala makes constructor parameters directly available to the methods in the class (I'm not sure why), so if you use the same name you will find that your code fails to compile with the error reassignment to val when you try to reassign the "var". For example in the following snippet, the immutable constructor parameter shadows the inherited var and so you can't assign to the parameter.

class B (val name: String, updateCount: Int) extends A(updateCount) {
  def inc(): Unit = {
    updateCount = updateCount + 1
  }
}
Ken Bloom
class B(val name:String, updateCount:Int) extends A(updateCount) will not compile for me. I get (fragment of extend.scala):7: error: reassignment to val updateCount = updateCount + 1
trenton
The first version compiles. The second version intentionally does not, and demonstrates the problem with using the same name for the var and the constructor parameter.
Ken Bloom
A: 

As others mention, your constructor param in B is shadowing the superclass' version. Here's the code that works and doesn't require self-type:

class A (var updateCount: Int) {
}

class B (val name: String, uc: Int) extends A(uc) {
  def inc(): Unit = {
    updateCount = updateCount + 1
  }
}
davetron5000