smeeb
smeeb

Reputation: 29537

Scala primary constructors and the fields/properties they generate

New to Scala and no matter how many articles/tutorials I read (like these | three | ones) I can't seem to wrap my brain around how constructors work.

Let's take three examples of a primary constructor here:

// 1
class Fizz(buzz : Buzz) { ... }

// 2
class Fizz (val buzz : Buzz) { ... }

// 3
class Fizz (var buzz : Buzz) { ... }

For each of these:

Upvotes: 3

Views: 1343

Answers (2)

francoisr
francoisr

Reputation: 4595

Constructors are actually a pretty wild beast in Scala. In fact the Scala compiler is able to make some smart choices for you.

By the phrasing of your question, I take it you probably have some experience with Java. To make it perfectly clear what's happening - and allow you to experiment yourself in the future - let's decompile the code that the Scala compiler produces so we can see the Java equivalent. For conciseness, I'll only show the declarations of the methods/fields, and not their implementation.

No qualifier, buzz used only in constructor

class Fizz(buzz : Buzz)

Will compile to the Java equivalent of

public class Fizz {
    public Fizz(Buzz);
}

Since Fizz doesn't declare any method/field referencing buzz (apart from the constructor itself), Scala will not create any field/method for it.

No qualifier, buzz referenced outside the constructor

class Fizz(buzz : Buzz) {
    def foo: Buzz = buzz
}

This time buzz is used by the foo method, and so the compiler has to store it as a field. Since the field was not declared a var, it will be made private final.

public class Fizz {
  private final Buzz buzz;
  public Buzz foo();
  public Fizz(Buzz);
}

val qualifier

class Fizz(val buzz : Buzz)

This time, you explicitly said you wanted buzz to be a val. This will have the effect of creating a private final field to hold buzz, and a public method of the same to access it.

public class Fizz {
  private final Buzz buzz;
  public Buzz buzz();
  public Fizz(Buzz);
}

var qualifier

class Fizz(var buzz : Buzz)

This case is very similar to the previous one, except that now you're specifying that you want to be able to modify buzz. This will cause the Scala compiler to provide a setter method for you, under the funny name of buzz_$eq. The $ shenanigan is only required because of JVM constraints in naming methods. In your Scala code, this method will appear as buzz_= and syntactic sugar will allow you to call it as fizz.buzz = someBuzz. This way, it actually looks as if you're mutating as field, but you're really only calling a setter.

public class Fizz {
  private Buzz buzz;
  public Buzz buzz();
  public void buzz_$eq(Buzz);
  public Fizz(Buzz);
}

How to decompile Scala?

These commands are helpful when investigating that kind of issue:

scalac Fizz.scala

will create a compiled Fizz.class that is not human-readable. You can decompile it into a Java version using

javap -constants -p Fizz

while in the directory containing Fizz.class

Upvotes: 7

freedev
freedev

Reputation: 30157

In all the cases you showed the fields are also parameters for the constructor.

The parameters declared to be either val or var become public members. If you use the variables in the constructor they will not become members, if you use them in the class, they will be private members.

In first case class Fizz(buzz : Buzz){} the buzz parameter is immutable and not become member (I assume you don't use it anywhere).

In second case class Fizz (val buzz : Buzz) {} the buzz parameter is immutable and become public member.

In third case class Fizz (var buzz : Buzz) {} the buzz parameter is mutable and become public member.

And again in all the cases there isn't any getter or setter created automatically.

Upvotes: 0

Related Questions