Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need clarification in constructor definition syntax in scala

Tags:

scala

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?

like image 736
Raj Avatar asked Oct 06 '12 07:10

Raj


2 Answers

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
}
  1. The first parameter, 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).
  2. The second, prvt, is referenced by a method, so it becomes a private field.
  3. The val declarator on the third creates a getter and a private backing field.
  4. The 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.

like image 51
outis Avatar answered Nov 15 '22 09:11

outis


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);
}
like image 40
Brian Avatar answered Nov 15 '22 09:11

Brian