Why control access
Up to now every field was visible to all code, which is convenient but leaks the internals of a class. If code everywhere reads and writes a field directly, then changing how that field works later means changing every place that touches it. An access modifier says who can see a member of a class.
The idea behind this is encapsulation: an object's data is hidden behind methods. The class decides how the data is read and changed, and outside code sees only the methods.
The modifiers in Scala
| Modifier | Who can use the member |
|---|---|
| (none) | Everyone. Scala has no public keyword, because this is the default. |
private | Only code inside the class itself (and its companion object). |
protected | The class and its subclasses. |
For now, focus on the first two. Scala can also limit access to a package with a qualifier such as private[mypackage], but this lesson doesn't need it.
A private field
A private member can be used inside the class but not outside it:
class Dog(private val name: String)
The parameter is marked private val: it becomes a field, but only the class can see it. Outside the class, Dog("x").name doesn't compile, and the compiler explains:
private value name can only be accessed from class Dog.
The same applies to a private var declared in the class body. Assigning to it from outside is rejected in the same way.
Getters
If outside code needs the value, give it a public method. Methods that return a field are called getters:
class Dog(private val name: String) {
def getName(): String = {
return name
}
}
Inside the class, name is accessible directly, so getName can return it. Callers use rex.getName() and never touch the field. Scala would also accept the bare expression name as the method's body, without return, since the last expression is the result.
A getter is also a place to add rules. A class can hide a counter and offer only a controlled way to change it:
class Dog(private val name: String) {
private var energy: Int = 10
def run(): Unit = { if (energy > 0) energy -= 1 }
def getEnergy: Int = energy
}
Outside code can call run() and read getEnergy, but cannot set energy to a negative number. After one call of run() on a new dog, getEnergy is 9.
Because private works per class, not per object, a dog's method can read another dog's private field: inside Dog, a method taking o: Dog may use o.name. That is how a method like same(o: Dog): Boolean = name == o.name works.
protected
A protected member is hidden from the outside but available to subclasses. In a class Dog with protected def secret(): String = "s", a subclass such as Puppy can call secret(), while Dog().secret() from unrelated code is rejected with method secret cannot be accessed as a member of Dog. Subclasses are covered in the next lessons.
The exercise
The starter code gives Dog a private val name, and mainFunction calls rex.getName(). Add the getter:
class Dog(private val name: String) {
def getName(): String = {
return name
}
}
class Solution {
def mainFunction(): Unit = {
val rex = Dog("Rex")
println("Dog's name: " + rex.getName())
}
}
The output is Dog's name: Rex.
Common mistakes
- Reading the private field from outside.
rex.nameinmainFunctiondoesn't compile. Userex.getName(). - Writing
public. Scala has no such keyword. Leave the modifier out. - Procedure syntax.
def getName() { return name }without the: String =part doesn't compile in Scala 3, which reports'=' expected, but '{' found. - Using
valwhen avaris needed. Aprivate valnever changes. Use aprivate varfor state that methods change.
Key takeaways
- Members are public by default, and
privatehides them from code outside the class. - Keep data private and offer public methods to read or change it.
protectedalso lets subclasses use a member.- A getter is an ordinary method that returns a private field.
