In Scala, fields are declared in the primary constructor. And, if it happens to have the same name as a field from the super class, it will use the one passed in instead, which is what I am trying to avoid. Take the following example:
class Parent(protected val changedParam: MyClass)
class Child(changedParam: MyClass) extends Parent(doStuff(changedParam)) {
def foo() = {
bar(changedParam) // Uses Child.changedParam, not Parent.changedParam, which is what I want
}
}
I want to make changedParam in Child to refer to the changedParam in Parent, not Child. Is there any way to do that?
A parameter of a class is visible in its body. It will be made a private val if need be, that is if it is used in a method, and not just in the initialization code (and of course if it is not directly declared a val with a different visibility, or a var).
So changedParam
in Child
shadows the one in Base
. The obvious way to avoid that is simply to call it another name:
class Child(anotherName: MyClass) extends Base(doStuff(anotherName)) {...
This is considered brittle and annoying.
You get a little relief if you're shadowing a var:
scala> class A { var a = 1 } ; class B(a: Int) extends A { def f = a }
defined class A
defined class B
scala> val b = new B(42) ; b.a = 7 ; b.f
b: B = B@1376c05c
b.a: Int = 7
res0: Int = 42
scala> :replay -Xlint
Replaying: class A { var a = 1 } ; class B(a: Int) extends A { def f = a }
<console>:7: warning: private[this] value a in class B shadows mutable a inherited from class A. Changes to a will not be visible within class B - you may want to give them distinct names.
class A { var a = 1 } ; class B(a: Int) extends A { def f = a }
^
defined class A
defined class B
Replaying: val b = new B(42) ; b.a = 7 ; b.f
b: B = B@f2ff811
b.a: Int = 7
res0: Int = 42
Normally, shadowing a val is more benign, but a warning for your example would be useful.
class Parent(protected val changedParam: MyClass)
trait Child extends Parent {
def foo() = {
bar(changedParam)
}
}
object Child {
def apply(changedParam: MyClass): Child =
new Parent(doStuff(changedParam)) with Child
}
Not much to say... Turning Child
into a trait
avoids to declare a new temporary member that you don't want to be used after the initialization. Then I used the companion object to declare the constructor. The only difference in usage is that you don't have to add the new
keyword to instanciate Child
.
Test:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Parent(protected val changedParam: Int)
trait Child extends Parent {
def foo() = {
println(changedParam)
}
}
object Child {
def apply(changedParam: Int): Child =
new Parent(1 + changedParam) with Child
}
// Exiting paste mode, now interpreting.
defined class Parent
defined trait Child
defined object Child
scala> Child(42).foo()
43
1) Using explicit call to Parent
with asInstanceOf
:
package demo {
// remove protected or make it protected[demo]
class Parent(protected[demo] val changedParam: MyClass)
class Child(changedParam: MyClass)
extends Parent(doStuff(changedParam)) {
def foo() = {
bar(this.asInstanceOf[Parent].changedParam)
}
}
}
2) Using early initializers syntax:
class Parent(protected val changedParam: MyClass)
class Child(changedParam: MyClass) extends {
val superChangedParam = doStuff(changedParam)
} with Parent(superChangedParam) {
def foo() = {
bar(superChangedParam)
}
}
Nevertheless the best solution is to give a different name to your param.