1. Introduction
Scala has a system of access modifiers. In this tutorial, we’ll look at these access modifiers and discuss their scope (package, class, and object).
2. Class and Object Scope
If a class member is public, no access modifier is required in the definition. In fact, Scala doesn’t have a public keyword. This is the default access if we don’t specify any modifier, and is equivalent to the Java public access.
However, private fields are required to be labeled as such:
class Rectangle(widthParam: Int, heightParam: Int, colorParam: String) {
val width: Int = widthParam
val height: Int = heightParam
val color: String = colorParam
private val numberOfVertexes: Int = 4
//...
}
In this example, the private member numberOfVertexes is accessible only within the class.
Scala also allows us to protect members from being accessed outside the object scope using the private[this] modifier:
abstract class Figure {
//...
private[this] val code = randomUUID.toString
def printCode: Unit = println(s"$code")
def compareCodes(that: Figure) = {
this.code == that.code //that.code is not accessible
}
}
The modifier private[this] is the most strict modifier, and as a result, the field is only accessible by the instance of the class.
3. Package Scope
3.1. The protected Modifier
Members marked protected are only accessible in the base class and its subclasses. Let’s change the modifier of the member color to protected in the abstract class Figure:
abstract class Figure {
//...
protected val color: String
}
The field color is no longer accessible outside the scope of the base class and its subclasses. Attempting to access the color field somewhere outside the base class and subclasses will cause a compile-time error.
3.2. The protected[packagename] Modifier
We can also make a member accessible by reference in the package itself and the nested packages. In this case, we need to change the access modifier of color to protected[accessmodifiers] (where accessmodifiers is the package name) in the abstract class Figure:
abstract class Figure {
//...
protected[accessmodifiers] val color: String
}
class Composition(fig1: Figure, fig2: Figure) {
val color = mixColors(fig1.color, fig2.color)
//...
}
3.3. The protected[this] Modifier
Members defined with the protected[this] modifier are only accessible in the instance they are defined in, and the instances of the sub-classes:
abstract class Figure {
//...
protected[this] val lineWidth: Int
}
The member lineWidth only accessible in the class instance and in the sub-class instance. It is not accessible in other instances.
4. Nested Classes Scope
In the case of nested classes, access rules are slightly different from the rules in Java.
Suppose that we have a class Rectangle, which contains a private Boolean field, isSquare, and a nested class called InnerFigure:
class Rectangle(widthParam: Int, heightParam: Int, colorParam: String) extends Figure {
//...
private val isSquare = width == height
class InnerFigure(fig: Figure) {
private val codeInner = randomUUID.toString
val isInsideSquare = isSquare
def printCodeInner = print(codeInner)
}
}
In this case, isSquare is accessible from the inner class. However, the private field codeInner is not accessible from the outer class (note that in Java this is not the case.).
5. Companion Object Access
Let’s suppose that the class Star (defined by the vertexes coordinates) has a companion object. In this case, all members of the companion object are accessible inside the companion class by import, regardless if they are private or not, and none of the companion class members are accessible in the companion object:
class Star(val vertexes: Map[Int, (Double, Double)], val color: String) extends Figure {
import Star._
override val lineWidth: Int = 1
//...
val x = vertexes.values.toList.map(_._1)
override protected val rightMostPoint = x.max
val area = areaByVertexes(vertexes)
}
object Star {
def apply(vertexes: Map[Int, (Double, Double)], color: String) =
new Star(vertexes, color)
private def areaByVertexes(vertexes: Map[Int, (Double, Double)]): Double = ??? // implemented somehow
val right = rightMostPoint // Cannot resolve symbol rightMostPoint
}
Note, that import Star._ in the companion class provides access to the members of companion object in the companion class only.
6. Conclusion
In this article, we examined the different access modifiers in Scala. Additionally, we also saw how they relate to package, class, and object scopes.
As usual, these examples are available over on GitHub.