A quick overview of access modifiers in Scala
The available access modifiers in Scala are closely related to the ones in Java, but there are some important differences.
It’s still a reasonably fair assumption that those who are learning Scala already know Java. At least that’s the assumption I’m making this article.
In Java, we have
- Public, with the
public
reserved word in classes, tacit for interface functions and procedures - Protected, with the
protected
reserved word - Package private (no reserved word)
- Class private, with the
private
reserved word
Essentially this means that for Java the default access level in classes is package private. Maybe this was meant to encourage programmers to consider the single responsibility principle as it relates to packages, but mostly it confuses students.
Scala has more access levels with fewer reserved words.
- Public, no reserved word but the same effect as Java’s public
- Enclosing package protected, with the
protected[
x]
syntax, where x is the name of the package - Protected, with the
protected
reserved word but a subtly different meaning from Java’s - Instance protected, with the
protected[this]
syntax - Enclosing package private, with the
private[
x]
syntax, where x is the name of the enclosing package - Package private, with the
private[
x]
syntax, where x is the name of the package the class, object or trait is in - Class private, with the
private
reserved word - Instance private, with the
private[this]
syntax
This means that public access is the default in Scala. Given my earlier assumption about your knowledge of Java, I’ve decided not to explain public any further in this article.
Protected does need explanation even with the assumption. Protected in Java means that it can be accessed in subclasses and in the package that it’s defined in.
For example, java.util.Random
provides this function:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong currseed = this.seed;
do {
oldseed = currseed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!currseed.compareAndSet(oldseed, nextseed));
return (int) (nextseed >>> (48 - bits));
}
We can extend java.util.Random
in any package we want, like for example org.example.math
, and those subclasses can call next()
, because it’s marked as protected.
But all other classes and interfaces in java.util
can also call next()
, even classes and interfaces that have no particular reason to do so, like java.util.ArrayList
or java.util.Currency
, to give just two examples.
Now let’s turn our attention to scala.util.Random
(you can see the source for the Scala 2.12.4 version on GitHub). There’s nothing protected in that Random
, or private for that matter. But let’s pretend that there is, let’s say the fictional function exemplify()
is protected.
Then subclasses of scala.util.Random
could call exemplify()
, no matter what package they may be in. But MurmurHash
wouldn’t be able to call exemplify()
, even though MurmurHash
is also in the scala.util
package, because it doesn’t extend Random
.
However, we could grant Java-like protected access for exemplify()
to all classes, objects and traits in scala.util
by declaring exemplify()
as protected[util]
.
Also note that the Scala compiler takes a different view of “nested” packages than the Java compiler.
For example, to the Java compiler, java.util
, java.util.concurrent
and java.util.concurrent.atomic
are distinct packages that coincidentally happen to share letters in their names. Import statements are needed in the “subpackages” of java.util
, or else we must use fully qualified names.
To the Scala compiler, on the other hand, scala.util.control
is a subpackage of scala.util
, and classes in scala.util.control
don’t need import statements for classes, objects or traits at the scala.util
level.
I’ll have to delve into instance protected in another article. The concept is similar to instance private, which I will explain in this article but not in much detail.
Class private is the same in Scala as in Java. Though if you write immutable classes initialized with val
fields, you might feel less of a need to mark things class private.
Instance private is the most restrictive access level in Scala. Items so marked can be accessed by their instances, but not by other instances of the same class.
For example, suppose we define a Scala class called ToyExample
with an instance private variable called counter
. Suppose we have two instances, A
and B
, of ToyExample
. Then A
can access its own counter
, but not B
’s counter
, and likewise B
can access its own counter
, but not A
’s.
Obviously, we wouldn’t be able to use counter
for ToyExample
’s equals()
or hashCode()
overrides. We’d have to broaden the access to class private.
I advise Java developers to choose the most restrictive access they can justify. Scala developers, on the other hand, should still be deliberate about access levels, but they can worry about it much less if they mostly write immutable classes.
I don’t know how Scala enforces its more fine-grained access levels, but that’s a topic for another article.