How to control Scala method scope with private, package, and more

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 5.1, “How to control method scope in Scala (private, package, more).”

Problem

Scala methods are public by default, and you want to control their scope in ways similar to Java.

Solution: Scala access modifiers (scopes)

Scala lets you control method visibility in a more granular and powerful way than Java. In order from “most restrictive” to “most open,” Scala provides these scope options:

  • Object-private scope
  • Private
  • Package
  • Package-specific
  • Public

These scopes are demonstrated in the examples that follow.

Object-private scope

The most restrictive access is to mark a method as “object-private.” When you do this, the method is available only to the current instance of the current object. Other instances of the same class cannot access the method.

You mark a method as object-private by placing the access modifier private[this] before the method declaration:

private[this] def isFoo = true

In the following example, the method doFoo takes an instance of a Foo object, but because the isFoo method is declared as an object-private method, the code won’t compile:

class Foo {
    private[this] def isFoo = true
    def doFoo(other: Foo) {
        if (other.isFoo) {  // this line won't compile
            // ...
        }
    }
}

The code won’t compile because the current Foo instance can’t access the isFoo method of the other instance, because isFoo is declared as private[this]. As you can see, the object-private scope is extremely restrictive.

Private scope

A slightly less restrictive access is to mark a method private, which makes the method available to (a) the current class and (b) other instances of the current class. This is the same as marking a method private in Java. By changing the access modifier from private[this] to private, the code will now compile:

class Foo {
    private def isFoo = true
    def doFoo(other: Foo) {
        if (other.isFoo) {  // this now compiles
            // ...
        }
    }
}

By making a method private, it is not available to subclasses. The following code won’t compile because the heartBeat method is private to the Animal class:

class Animal {
    private def heartBeat {}
}

class Dog extends Animal {
    heartBeat  // won't compile
}

Protected scope

Marking a method protected makes the method available to subclasses, so the following code will compile:

class Animal {
    protected def breathe {}
}

class Dog extends Animal {
    breathe
}

The meaning of protected is slightly different in Scala than in Java. In Java, protected methods can be accessed by other classes in the same package, but this isn’t true in Scala. The following code won’t compile because the Jungle class can’t access the breathe method of the Animal class, even though they’re in the same package:

package world {
    class Animal {
        protected def breathe {}
    }
    class Jungle {
        val a = new Animal
        a.breathe   // error: this line won't compile
    }
}

Package scope

To make a method available to all members of the current package — what would be called “package scope” in Java — mark the method as being private to the current package with the private[packageName] syntax.

In the following example, the method doX can be accessed by other classes in the same package (the model package), but the method doY is available only to the Foo class:

package com.acme.coolapp.model {
    class Foo {
        private[model] def doX {}
        private def doY {}
    }
    class Bar {
        val f = new Foo
        f.doX  // compiles
        f.doY  // won't compile
    }
}

More package-level control

Beyond making a method available to classes in the current package, Scala gives you more control and lets you make a method available at different levels in a class hierarchy. The following example demonstrates how you can make the methods doX, doY, and doZ available to different package levels:

package com.acme.coolapp.model {
    class Foo {
        private[model] def doX {}
        private[coolapp] def doY {}
        private[acme] def doZ {}
    }
}

import com.acme.coolapp.model._

package com.acme.coolapp.view {
    class Bar {
        val f = new Foo
        f.doX  // won't compile
        f.doY
        f.doZ
    }
}

package com.acme.common {
    class Bar {
        val f = new Foo
        f.doX  // won't compile
        f.doY  // won't compile
        f.doZ
    }
}

In this example, the methods can be seen as follows:

  • The method doX can be seen by other classes in the model package (com.acme.coolapp.model).
  • The method doY can be seen by all classes under the coolapp package level.
  • The method doZ can be seen by all classes under the acme level.

As you can see, this approach allows a fine-grained level of access control.

Public scope

If no access modifier is added to the method declaration, the method is public. In the following example, any class in any package can access the doX method:

package com.acme.coolapp.model {
    class Foo {
      def doX {}
    }
}

package org.xyz.bar {
    class Bar {
        val f = new com.acme.coolapp.model.Foo
        f.doX
    }
}

Discussion

The Scala approach to access modifiers is different than Java. Though it offers more power than Java, it’s also a little more complicated.

Table 5-1 describes the levels of access control that were demonstrated in the examples in the Solution.

Table 5-1. Descriptions of Scala’s access control modifiers

Access modifier Description
private[this] The method is available only to the current instance of the class it’s declared in.
private The method is available to the current instance and other instances of the class it’s declared in.
protected The method is available only to instances of the current class and subclasses of the current class.
private[model] The method is available to all classes beneath the com.acme.coolapp.model package.
private[coolapp] The method is available to all classes beneath the com.acme.coolapp package.
private[acme] The method is available to all classes beneath the com.acme package.
(no modifier) The method is public.