views:

270

answers:

5

In Scala, how does one define a local parameter in the primary constructor of a class that is not a data member and that, for example, serves only to initialize a data member in the base class?

For example, in the following code, how could I properly define parameter b in the primary constructor of class B so that it generates only a temporary local parameter and not a data member?

class A(var a: Int)
class B(?b?) extends A(b)
+6  A: 

Derek,

If you remove the "var" or "val" keyword from the constructor parameter, it does not produce a property.

Be aware, though, that non-var, non-val constructor parameters are in-scope and accessible throughout the class. If you use one in non-constructor code (i.e., in the body of a method), there will be an invisible private field in the generated class that holds that constructor parameter, just as if you made it a "private var" or "private val" constructor parameter.

Randall Schulz

Randall Schulz
Randall, thank you for your answer. Can you please provide an example of your caveat?
Derek Mahar
+4  A: 

After some experimentation, I determined that simply leaving out var or val in front of the parameter b will make it a local parameter and not a data member:

class A(var a: Int)
class B(b: Int) extends A(b)

Java expansion:

$ javap -private B
Compiled from "construct.scala"
public class B extends A implements scala.ScalaObject{
    public B(int);
}

$ javap -private A
Compiled from "construct.scala"
public class A extends java.lang.Object implements scala.ScalaObject{
    private int a;
    public A(int);
    public void a_$eq(int);
    public int a();
    public int $tag()       throws java.rmi.RemoteException;
}

Notice that class A has a private data member a due to the var a: Int in its primary constructor. Class B, however, has no data members, but its primary constructor still has a single integer parameter.

Derek Mahar
+3  A: 

Derek,

If you have this:

class A(a: Int) {
  val aa = a // reference to constructor argument in constructor code (no problem)
  def m: Float = a.toFloat // reference to constructor argument in method body (causes a to be held in a field)
}

you'll find (using javap, e.g.) that a field named "a" is present in the class. If you comment out the "def m" you'll then see that the field is not created.

Randall Schulz
A: 

Randall, your answers explain why the Scala compiler complains when I introduce a method inc that increments the property a, but also change the name of the parameter in the class B constructor to match that of the parameter in the class A constructor:

class A(var a: Int)
class B(a: Int) extends A(a) {
  def inc(value: Int) { this.a += value }
}

Scala compiler output:

$ scala construct.scala
construct.scala:3: error: reassignment to val
  def inc(value: Int) { this.a += value }
                               ^
one error found

Scala complains because class B must now have a private, read-only property a due to the reference to a in inc. Changing B(a: Int) to B(var a: Int) generates a different compiler error:

construct.scala:2: error: error overriding variable a in class A of type Int;
 variable a needs `override' modifier
class B(var a: Int) extends A(a) {
            ^
one error found

Adding override doesn't help, either:

construct.scala:2: error: error overriding variable a in class A of type Int;
 variable a cannot override a mutable variable
class B(override var a: Int) extends A(a) {
                 ^
one error found

How can I use the same name in the parameter in the primary constructor of B as the property defined in the primary constructor of the base class A?

Derek Mahar
As far as I know (which ain't all that far), it's not possible.
Randall Schulz
+1  A: 

You can create temporary variables throughout the initialization of single class members like this:

class A(b:Int){
  val m = {
    val tmp = b*b
    tmp+tmp
  }
}
ziggystar
So `m` is a property that an instance of class `A` initializes to `2*b*b`?
Derek Mahar
Exactly. You can also wrap something in curly brackets inside the constructor. Maybe locals instantiated there will also not be created as class members.
ziggystar