The code below gives a compile error:
class Info(val x: String)
object Info {
val default = new Info("top")
}
case class Data(x: String) {
import Info.default
def this() = this(default.x)
}
Error:(11, 23) not found: value default def this() = this(default.x)
Why is symbol default
not seen in the constructor, in spite of the import?
Further experiments show it is not only import. Replacing import line with a def
(or even val
) does not help, still the error persists:
def default = Info.default
We are allowed to create any number of auxiliary constructors in our Scala class, but a scala class contains only one primary constructor. Auxiliary constructors are defined as methods in the class with the keyword this. We can describe multiple auxiliary constructors, but they must have different parameter lists.
The auxiliary constructor in Scala is used for constructor overloading and defined as a method using this name. The auxiliary constructor must call either previously defined auxiliary constructor or primary constructor in the first line of its body.
The primary constructor of a Scala class is a combination of: The constructor parameters. Methods that are called in the body of the class. Statements and expressions that are executed in the body of the class.
Scoping doesn't work the way you expected because of the scoping of self constructor invocations (when secondary constructors invoke the primary constructor):
The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class and by any early definitions of the enclosing template.
In other words, the scope in effect for the expression default.x
is the scope outside Data
.
This is confusing because textually it looks like that expression is inside the class.
This rule has to do with lexical scope and not with evaluation order.
You have to move the import outside the class definition.
For fun, scoping is slightly broken in the following case:
object Playroom {
def default = "playing"
case class Toy(squeezeMsg: String = default)
object Toy {
def default = "srsly"
}
}
object Test extends App {
import Playroom._
println(Toy())
println(new Toy())
}
which is this puzzler. Generally speaking, it's more idiomatic (and robust) to use apply
methods on the companion.
This is behaving according to specs. The body of the class is part of the so called primary constructor. The auxiliary constructor(s) needs to start by calling the primary or another auxiliary defined before. Assuming you only have one auxiliary, since the call to primary must be the first statement in your auxiliary, it means that during this first statement you do not have access to anything defined inside the primary. After the first call is done, then you can access any of the members and imports defined in the primary.
In your example, the problem is that the import (import Info.default
) is not visible to the first statement of your auxiliary. If you replace it with def this() = this(Info.default.x)
it should work.
The following compiles fine since the call to Constant
is done after the call to the primary.
class G(i: Int) {
val Constant = 1
// No argument auxiliary constructor
def this() = {
this(3) // Call to primary
println(Constant * 2)
}
}
The following does not compile since the call to Constant
is done before the call to the primary, which means that Constant
has not been created yet.
class G(i: Int) {
val Constant = 1
// No argument auxiliary constructor
def this() = {
this(Constant) // This will cause an error!
}
}
Solution:
Have any constants you need defined in the companion object. The companion object is by definition initialized first, so you will be able to access any members of it from within the constructors of your class.
object G {
val Constant = 1
}
class G(i: Int) {
// No argument auxiliary constructor
def this() = {
this(G.Constant) // This now works!
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With