scala class constructor parameters
Solution 1
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
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
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
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 =
zihaoyu
Currently Software engineer at HBO, formerly worked at Gilt Groupe. MS Computer Engineering graduate from Rutgers University.
Updated on March 02, 2020Comments
-
zihaoyu about 4 years
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
var
s, 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 } }
-
Randall Schulz about 11 yearsIt's somewhat more subtle than a public / private distinction. Without a property keyword (
val
orvar
) the constructor parameters will only be stored in a field at all if they're used in a method body. If they're used only in constructor code and are not prefixed withvar
orval
, then they have no existence beyond the constructor at all. If size of an instance matters, be aware of whether your non-property constructor parameters are referenced in method bodies. -
Randall Schulz about 11 yearsAlso, scope and accessibility are different things.
-
Ernesto over 9 years@RandallSchulz how is it possible then that this Scala code compiles and works? (gist.github.com/gnapse/96387a056f0cf45dac7a). The
fake
argument of theTesting
class is not prefixed byval
orvar
and it is still accessible inside instance methods (check out theoutput
method), not just in the constructor code. -
Code Pope over 7 yearsThe first comparision is not completely right. In the Java code you just can call the parameters
name
andage
in the constructor, not in other class methods, but in Scala you can. The similar code in Java would be:class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } }
-
Code Pope over 7 yearsAddition: The two fields are immutable, too.
-
Nader Ghanbari over 6 yearsScala compiler will emit a private field in this case, if you look at the resulting bytecode you will see
private final Z fake
.