In Scala, how do you define a local parameter in the primary constructor of a class?
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)
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) exte开发者_C百科nds 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
?
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.
Addendum (better late than never??):
In this code the references to the constructor parameter occur only in the constructor body:
class C1(i: Int) {
val iSquared = i * i
val iCubed = iSquared * i
val iEven = i - i % 2
}
... Here the value i
exists only during the execution of the constructor.
However, in the following code, because the constructor parameter is referenced in a method body—which is not part of the constructor body—the constructor parameter must be copied to a (private) field of the generated class (increasing its memory requirement by the 4 bytes required to hold an Int
):
class C2(i: Int) {
val iSquared = i * i
val iCubed = iSquared * i
val iEven = i - i % 2
def mod(d: Int) = i % d
}
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.
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
}
}
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.
精彩评论