I'm new to scala and need clarification on the following code snippet involving constructors for classes.
class sample (a: Int, b: Int) {
/* define some member functions here */
}
Can I take that the variables a
and b
are private to the class sample
?
class sample (val a: Int, val b: Int) {
/* define some member functions here */
}
And in this case, are a
and b
publicly accessible? What is the exact effect of adding the val
keyword in the parameter list for the constructor? And if I use the def
keyword instead of val
, does it have the same effect as well?
The following Scala class shows four possibilities for constructor parameters (3 different declarations, one of which has two effects):
class ConstructorParams (local:Int, prvt:Int, val readonly:Int, var writable:Int) {
def succ_prvt() = prvt + 1
}
local
, isn't referenced by any methods. It exists only as a local variable within the constructor (it could be referenced by field initialisers without changing this; try adding val foo = local
to the constructor).prvt
, is referenced by a method, so it becomes a private field.val
declarator on the third creates a getter and a private backing field.var
declarator on the fourth creates both a getter and a setter, as well as a private backing field.In particular, note that a constructor parameter without a val
or var
declarator is private (which is a matter of accessibility) only if it's referenced in a function within the class; otherwise, it's local (which is a matter of scope).
Technically, the last three parameters exist as local values as well as private fields (initialized to the local values), but this distinction shouldn't come up much, so you can mostly put it out of your mind.
def
as a parameter declarator doesn't make much sense as it's used to introduce a new function, not to declare a value/variable name; it also isn't used for function parameters (constructor parameters are closely related to function parameters). Since functions are a first class, you use a function type, rather than a special declarator, to declare that a parameter holds a function.
Printing out what the compiler makes of ConstructorParams
by passing -Xprint:constructors
to the compiler, we get (with comments added):
class ConstructorParams extends Object {
/* 2nd (no-declarator) param becomes private field due to reference in `succ_prvt()` */
<paramaccessor> private[this] val prvt: Int = _;
/* `val` becomes private field + getter */
<paramaccessor> private[this] val readonly: Int = _;
<stable> <accessor> <paramaccessor> def readonly(): Int = ConstructorParams.this.readonly;
/* `var` becomes private field + getter + setter */
<paramaccessor> private[this] var writable: Int = _;
<accessor> <paramaccessor> def writable(): Int = ConstructorParams.this.writable;
<accessor> <paramaccessor> def writable_=(x$1: Int): Unit = ConstructorParams.this.writable = x$1;
/* causes `prvt` constructor param to become private field */
def succ_prvt(): Int = ConstructorParams.this.prvt.+(1);
def <init>(local: Int, prvt: Int, readonly: Int, writable: Int): ConstructorParams = {
ConstructorParams.this.prvt = prvt;
ConstructorParams.this.readonly = readonly;
ConstructorParams.this.writable = writable;
ConstructorParams.super.<init>();
()
}
}
The above example class compiles to the Java equivalent of:
public class ConstructorParams {
/* 2nd (no-declarator) param becomes private field due to reference in `succ_prvt()` */
private final int prvt;
public int succ_prvt() {
return this.prvt + 1;
}
/* `val` becomes private field + getter */
private final int readonly;
public int readonly() { return this.readonly; }
/* `var` becomes private field + getter + setter */
private int writable;
public int writable() { return this.writable; }
public void writable_$eq(int x$1) {
this.writable = x$1;
}
/* 1st param is local, since it's not referenced in any other methods */
public ConstructorParams(int local, int prvt, int readonly, int writable) {
/* parent constructor is invoked implicitly, so not repeated here */
this.prvt = prvt;
this.readonly = readonly;
this.writable = writable;
}
}
If you use javap
on ConstructorParams (as Brian did) with the -p
argument, you'll see a class signature equivalent to the one in the above Java source.
class sample (a: Int, b: Int)
a and b are private in this case. Disassembling with javap shows a and b are not part of the class(Scala calls these class fields):
public class Sample extends java.lang.Object implements scala.ScalaObject{
public Sample(int, int);
}
a and b preceded with val. Disassembling with javap shows a and b are now public fields in Sample.
class sample (val a: Int, val b: Int)
public class Sample extends java.lang.Object implements scala.ScalaObject{
public int a();
public int b();
public Sample(int, int);
}
With def
instead of val
in the constructor it won't compile. def
is for defining functions. Not sure if you can use def
in constructor as a parameter.
Also, note that private and protected behave as you would expect. Given this:
class Sample(private val a: Int, protected val b: Int, val c: Int)
Disassembles to the following with javap:
public class Sample extends java.lang.Object implements scala.ScalaObject{
public int b();
public int c();
public Sample(int, int, int);
}
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