Scala 3 โ€” Book

Singleton Objects

Language

In Scala, the object keyword creates a Singleton object. Put another way, an object defines a class that has exactly one instance.

Objects have several uses:

  • They are used to create collections of utility methods.
  • A companion object is an object that has the same name as the class it shares a file with. In this situation, that class is also called a companion class.
  • Theyโ€™re used to implement traits to create modules.

โ€œUtilityโ€ methods

Because an object is a Singleton, its methods can be accessed like static methods in a Java class. For example, this StringUtils object contains a small collection of string-related methods:

object StringUtils {
  def isNullOrEmpty(s: String): Boolean = s == null || s.trim.isEmpty
  def leftTrim(s: String): String = s.replaceAll("^\\s+", "")
  def rightTrim(s: String): String = s.replaceAll("\\s+$", "")
}
object StringUtils:
  def isNullOrEmpty(s: String): Boolean = s == null || s.trim.isEmpty
  def leftTrim(s: String): String = s.replaceAll("^\\s+", "")
  def rightTrim(s: String): String = s.replaceAll("\\s+$", "")

Because StringUtils is a singleton, its methods can be called directly on the object:

val x = StringUtils.isNullOrEmpty("")    // true
val x = StringUtils.isNullOrEmpty("a")   // false

Companion objects

A companion class or object can access the private members of its companion. Use a companion object for methods and values which arenโ€™t specific to instances of the companion class.

This example demonstrates how the area method in the companion class can access the private calculateArea method in its companion object:

import scala.math._

class Circle(radius: Double) {
  import Circle._
  def area: Double = calculateArea(radius)
}

object Circle {
  private def calculateArea(radius: Double): Double =
    Pi * pow(radius, 2.0)
}

val circle1 = new Circle(5.0)
circle1.area   // Double = 78.53981633974483
import scala.math.*

class Circle(radius: Double):
  import Circle.*
  def area: Double = calculateArea(radius)

object Circle:
  private def calculateArea(radius: Double): Double =
    Pi * pow(radius, 2.0)

val circle1 = Circle(5.0)
circle1.area   // Double = 78.53981633974483

Creating modules from traits

Objects can also be used to implement traits to create modules. This technique takes two traits and combines them to create a concrete object:

trait AddService {
  def add(a: Int, b: Int) = a + b
}

trait MultiplyService {
  def multiply(a: Int, b: Int) = a * b
}

// implement those traits as a concrete object
object MathService extends AddService with MultiplyService

// use the object
import MathService._
println(add(1,1))        // 2
println(multiply(2,2))   // 4
trait AddService:
  def add(a: Int, b: Int) = a + b

trait MultiplyService:
  def multiply(a: Int, b: Int) = a * b

// implement those traits as a concrete object
object MathService extends AddService, MultiplyService

// use the object
import MathService.*
println(add(1,1))        // 2
println(multiply(2,2))   // 4

Contributors to this page: