Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When overriding a trait, why the value is strange?

Tags:

scala

traits

Demo scala code:

trait A {
  val a = 3
  val b = a + 2
}

trait B extends A {
  override val a = 10
}

object X extends B

println(X.b)

It prints value: 2, why is it not 5 or 12?

like image 745
Freewind Avatar asked Aug 05 '14 14:08

Freewind


People also ask

How do you override a trait?

The trait function can be overridden simply by defining a function with the same name in the class. Now (new MyClass)->sayHi(); will output "class says hi".

How do you override a trait method in Scala?

Overriding an implemented methodA class can also override a method that's defined in a trait. Here's an example: class Cat extends Pet { // override 'speak' override def speak(): Unit = println("meow") def comeToMaster(): Unit = println("That's not gonna happen.") }


1 Answers

To answer the why:

In Scala when you write

class A {
  val a = 2
}

The value is initialized in the constructor of the class (the same behavior applies to traits and objects). Furthermore, superclasses are initialized before subclasses. This leads to the following behavior for your use case:

B is created (memory is reserved), with two variables a and b whose value is 0. Now the constructor of A is invoked. Because a is overwritten in a subclass and due to Scalas dynamic binding nature, it is not assigned with 2, but with the value of the subclass. You want to be it 10, but because this assignment happens in the constructor of B (which is not yet invoked) the default value 0 is assigned. Now, b is assigned. Because it is not overwritten, the value a+2 is chosen, where a is 0. Because the constructor of A is finished here, the constructor of B can be invoked, which assigns 10 to a.

Hence, a is 10 and b is 2.

To answer what to do against this behavior bug:

Don't use vals as long as you don't absolutely understand about the problems that can arise. Use defs or lazy vals instead, there values are not initialized in the constructor of a class and therefore can be easily overwritten. If you absolutely need a val in a trait, then make it final

It is possible to mark a var as initialization independent for the subclass, which can be done with var a: Type = _. This tells the compiler to not initialize this variable in the constructor of the defining class (but means that the value needs to stay mutable). It can then easily be assigned in the subclass. This gets important when the in the constructor of the superclass as method is called, that initializes a var:

class A {
  f()
  def f() = ()
}

class B extends A {
  // don't initialize this var with anything else here or
  // the later assignment will be overwritten
  var b: Int = _
  override def f() =
    b = 5
}

new B().b // prints 5
like image 80
kiritsuku Avatar answered Sep 19 '22 03:09

kiritsuku