A quick overview of access modifiers in Scala

Alonso Del Arte
4 min readMar 26, 2024
Photo by Josh Appel on Unsplash

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.

--

--

Alonso Del Arte

is a Java and Scala developer from Detroit, Michigan. AWS Cloud Practitioner Foundational certified