The basics of Scala access modifiers

Alonso Del Arte
4 min readDec 11, 2020

--

Photo by Gleren Meneghin on Unsplash

Almost every Java student is taught early on that Java has four access levels: class private (often just “private”), package private, protected and public.

Something that is marked class private can only be accessed from the class that defines it. Something that is package private can be accessed from any class or interface (trait) in the same package.

Protected is package private plus all subclasses. And public members can be accessed from any class or interface, subject to caveats about modules for Java 9 and later.

Given that Martin Odersky designed Scala to interoperate with Java, it’s not surprising that Scala also has the concept of access levels, and access modifiers to specify those levels.

However, there are some differences and subtleties, which I will breeze through here, and illustrate only with toy examples, so as to pretty much only focus on the syntax.

I’m working on another article about access modifiers that also discusses motivation (when and why you would want to use different access levels). That other article will only have Java examples, but a lot of advice carries over to Scala with only slight adjustments.

Essentially, Scala has the same access levels as Java, but protected is subtly different, and a much more restrictive access level is added: instance private.

If a and b are instances of SomeClass, and someField is class private, then a can access a.someField and b.someField, and so can b. But if someField is instance private, a can access a.someField but not b.someField, and likewise b can access b.someField but not a.someField.

Clearly you can’t use instance private for fields that are needed in an equality comparison. Or you could if those fields have getters, but then what would be the point of declaring them instance private rather than class private?

An unfortunate consequence of the primary constructor of a Scala class being “fused” with the class declaration, in my opinion, is that the primary constructor can’t have local variables like in a Java class constructor, that are used in the constructor and then forgotten.

You can use class private fields in place of local variables, but then your IDE will probably suggest them as completions whenever an instance of a class accesses the fields of another instance of the same class, cluttering up the list of possible completions with options you don’t want.

Using instance private fields in a Scala class’s primary constructor might be a way to simulate local variables for a primary constructor, and help cut down on unwanted completion suggestions later on.

Package private in Scala is almost exactly the same as package private in Java, as long as there is no package or class nesting. Such nesting can result in many extra levels of access, which are convoluted or sophisticated, depending on your opinion.

There are also subtle differences between nested classes in Java and nested classes in Scala, but I think it goes well beyond the basics.

The modifier for class private is private, just like in Java. Instance private is designated as private[this]. And package private for a package called com.example is private[com.example].

Here’s a toy example to illustrate the three private levels:

package com.exampleclass ToyExample(initialCounter: Int) {
private[this] var counter = initialCounter // Instance private
private[com.example] val someField = ~initialCounter
// Package private
private def getCounter: Int = counter // Class private}

Protected access in Scala is different from protected access in Java in that it’s strictly only for the sake of inheritance.

Suppose SomeClass is in the com.example package. If SomeSubClass extends SomeClass, then SomeSubClass can access protected members of SomeClass even if the two are in different packages.

That’s the same as in Java, but in Scala, protected doesn’t overlap with package private. For example, given

package com.exampleclass SomeClass(private val label: String) {  protected def getLabel: String = this.label.toUpperCase}

the following won’t compile:

package com.exampleclass ToyExample(initialCounter: Int) {
// ...the fields, and getCounter() go here...
def attemptAccess(someObject: SomeClass): Unit = {
println(someObject.getLabel) // ERROR!!!
}
}

because getLabel() “is inaccessible from this place,” even though both ToyExample and SomeClass are in the com.example package.

Note that the SomeClass source illustrates the fact that access modifiers can appear in a parameter list: the label parameter for the constructor becomes a class private field.

I haven’t yet researched how these access modifiers operate across Java and Scala classes in the same or different packages, but I suspect the resulting complexity goes well beyond the basics.

Public is the same as in Java, except that its access modifier in Scala is always tacit (“public” is optional for functions and procedures defined in a Java interface, thus becoming essentially tacit in that context).

Martin Odersky realized, probably sooner than everyone else (from his work on the Java 1.3 compiler), that most Java programmers hardly ever use package private access.

So then it doesn’t make sense for package private to be the default access level, and that makes more sense for public.

This means I have already given examples of public: the classes SomeClass and ToyExample, and the procedure attemptAccess().

Maybe ditching the much-maligned semicolons saves more bytes in the source files, but in my opinion making the public access modifier tacit makes Scala feel a hell of a lot more concise than Java.

--

--

Alonso Del Arte
Alonso Del Arte

Written by Alonso Del Arte

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

No responses yet