scala class constructor parameters

ScalaConstructorScopeImmutability

Scala Problem Overview


What's the difference between:

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

and

class Person(val name: String, val age: Int) { 
  def say = "My name is " + name + ", age " + age
}

Can I declare parameters as vars, and change their values later? For instance,

class Person(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) {
    println("happy " + n + " birthday")
    n
  }
}

Scala Solutions


Solution 1 - Scala

For the first part the answer is scope:

scala> class Person(name: String, age: Int) {
     |   def say = "My name is " + name + ", age " + age
     | }

scala> val x = new Person("Hitman", 40)

scala> x.name
<console>:10: error: value name is not a member of Person
              x.name

If you prefix parameters with val, var they will be visible from outside of class, otherwise, they will be private, as you can see in code above.

And yes, you can change value of the var, just like usually.

Solution 2 - Scala

This

class Person(val name: String, val age: Int)

makes the fields available externally to users of the class e.g. you can later do

val p = new Person("Bob", 23)
val n = p.name

If you specify the args as var, then the scoping is the same as for val, but the fields are mutable.

Solution 3 - Scala

If you are familiar with Java, you can get the idea from this example:

class Person(name: String, age: Int)

is similiar to

class Person {
  public Person(String name, int age) {
  }
}

While

class Person(var name: String, var age: Int) // also we can use 'val'

is similar to

class Person {
  String name;
  int age;

  public Person(String name, int age) {
     this.name = name;
     this.age = age;
  }
}

The intuition is that without var/val, the variable is only accessible inside the constructor. If var/val is added, the class will have the member variables with the same name.

Solution 4 - Scala

The answers here are really good, however I'm tackling this one with exploring the byte code. When you apply javap on a class, it prints out package, protected, and public fields and methods of the classes passed. I created a class Person.scala and filled it out with the following code.

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVal(val name: String, val age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVar(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) = {
    println("happy " + n + " birthday")
    n
  }
}

After compiling the code with scalac Person.scala it generates three files with names Person.class, PersonVal.calass , PersonVar.cass. By running javap for each of these class files we can see how the structure would be:

>>javap Person.class
Compiled from "Person.scala"
public class Person {
  public java.lang.String say();
  public Person(java.lang.String, int);
}

In this case it didn't create any class varible for Person since it is declared with neither val, nor val so name and age can just be used inside constructor.

>>javap PersonVal.class
public class PersonVal {
  public java.lang.String name();
  public int age();
  public java.lang.String say();
  public PersonVal(java.lang.String, int);
}

In this case it has three members two for the input constructor and one for the member that we declared inside the constructore. However we don't have any setter for the input constructors so we can't change the values.

>>javap PersonVar.class
public class PersonVar {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public int happyBirthday(int);
  public PersonVar(java.lang.String, int);
}

It's the same as PersonVal example but we can change the values in this case with those variable_$eq methods. it nothing just a shortened version of variable =

Solution 5 - Scala

You could use a case class and in that case the Person class would have those variables available outside of the class. case class Person(name: String, age: Int). Then the following code would work as expected. val z = new Person("John", 20); z.name //John

Solution 6 - Scala

The answer by @Reza where the author explores byte code using javap helped me to clarify this concept the best. To cite a very specific example of this case please refer to below scenario that I faced in my production web app (Play + Scala): https://stackoverflow.com/questions/49034562/how-to-inject-parameters-into-a-class-trait-method-in-scala

If I don't use val prefix to injected parameter authorizationHandler then compiler throws this error:

class MyController needs to be abstract, since method authorizationHandler in trait AuthorizationCheck of type => controllers.authapi.AuthorizationHandler is not defined
[error] class MyController @Inject() (authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
[error]       ^
[error] one error found

Sadly the error didn't help me to pinpoint to the correct issue which is to prefix with val.

class MyController @Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
} 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzihaoyuView Question on Stackoverflow
Solution 1 - Scalaom-nom-nomView Answer on Stackoverflow
Solution 2 - ScalaBrian AgnewView Answer on Stackoverflow
Solution 3 - ScalayxjiangView Answer on Stackoverflow
Solution 4 - ScalaRezaView Answer on Stackoverflow
Solution 5 - ScalaTheM00s3View Answer on Stackoverflow
Solution 6 - ScalaNKMView Answer on Stackoverflow