Table of Contents
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. |
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |