2

Why does subclass create a copy of field when inheriting using a primary constructor?

For example

object Question
{
  class Animal(var name : String) {

    def setName(name : String) {
      this.name = name
    }
  }

  class Dog(name : String) extends Animal(name) {
    this.setName("dog: " + name)

    override def toString: String = this.name // this actually returns "Billy"
  }

  def main(args: Array[String]) {
    val dog = new Dog("Billy")
    println(dog.toString) // output "Billy", why?
    println(dog.name) // output "dog: Billy", why?
  }
}

As you can see, dog.toString() returns "Billy" instead of "dog: Billy", does it mean this.name is different from inherited name ? If so, then why does dog.name return "dog: Billy" ?

taffykula
  • 566
  • 7
  • 8

2 Answers2

4

If you run your compiler with the -Xlint option you should see the following.

warning: private[this] value name in class Dog shadows mutable name inherited from class Animal. Changes to name will not be visible within class Dog - you may want to give them distinct names.

If you rename the name argument to the Dog class, the warning goes away and the output is more consistent and perhaps closer to what's expected.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Ok. So primary constructor will create fields for whatever parameters passed in, isn't that a waste of resource if all I want is passing them to super class's constructor? – taffykula Dec 24 '15 at 21:48
  • I know that fact for a long time. And never stop wondering why there is no way to specify constructor argument without creating `private[this]` field behind the scene – ayvango Dec 24 '15 at 22:04
  • 1
    "isn't that a waste of resource if all I want is passing them to super class's constructor" But you don't: you use it in `toString`. That's why the compiler has to store it in a field. If you don't use the argument outside the constructor, no field should be created. – Alexey Romanov Dec 25 '15 at 06:47
  • 1
    @ayvango There is: just don't use the constructor argument outside the constructor. What there is not is a way to tell the compiler to make sure you don't use it accidentally (like there is with `@tailRec` or `@switch`). – Alexey Romanov Dec 25 '15 at 06:52
0

Here's your code debug result

your code debug result.

So, what you can see is that there are really actually two instances of variable "name". One is from "Animal", and one from "Dog". Constructors work a little bit different in Scala - what you call constructor arguments are actually local variables that are initialized with constructor arguments. I can't really provide any elegant solution to this, unfortunately. Also, I might be wrong. Please correct me if I am.

Update

Try this question.

Community
  • 1
  • 1
gordeevbr
  • 67
  • 8
  • It looks to me those constructor arguments are actually fields, since `this.name` returns "Billy". – taffykula Dec 24 '15 at 21:58
  • @taffykula, well, they are, actually. If you add `var` to `Dog.name`, you should get an error because of a name clash, see that post down bellow my answer for further explanations. So you should basically deal with how any constructor parameter is going to be a variable because this is how Scala ideology is. You could probably deal with this by renaming `Dog.name` to something else and assigning this value to `Animal.name`. After that just use `Animal.name` only. – gordeevbr Dec 24 '15 at 22:34
  • It doesn't look like I can edit the old comment. By saying "a local variable", I mean "a variable in scope of an instance" e.g. "a field", basically. – gordeevbr Dec 24 '15 at 22:44