1. Introduction

Objects in Scala are classes with exactly one instance and are created lazily only when we reference them somewhere else. We can define an object almost anywhere, but usually, we do that at the top level. When that’s the case, we can call the object a singleton.

Singletons are globally unique, and there will always be at most one instance of them running inside the JVM.

In this article, we’re going to see how we can define and use singleton objects in Scala.

2. Defining a Singleton

We can define a singleton by using the object keyword at the top level:

object Counter {
  private var counter: Int = 0
  val label: String = "CounterLabel"

  def increment(): Unit = {
    counter += 1
  }

  def get: Int = counter
}

In this case, the identifier Counter will always refer to the same instance and, likewise, Counter.get will always return the same counter integer. Notice that singletons, like all other Scala objects, don’t have a constructor.

Singleton objects effectively implement the singleton pattern in Scala with no additional effort. As shown in the previous example, they can contain both mutable and immutable states as well as methods. However, if any mutable state is defined, singletons are not thread-safe by default.

3. Using a Singleton

Singletons are instantiated lazily. Until we reference an object, for example, calling Counter.get, Counter.label, or Counter.increment(), no instance of Counter will exist on the heap. As soon as we reference one of those members, the runtime will execute the entire body of the singleton. This means creating the counter var, the label val, and defining the get and increment methods.

Let’s run our Counter example:

assert(Counter.get == 0)
assert(Counter.label == "CounterLabel")
Counter.increment()
assert(Counter.get == 1)
assert(Counter.label == "CounterLabel")

During the first call to Counter.get, the runtime creates the counter variable and sets it to 0. Additionally, it creates label, setting it to “CounterLabel”. Then, we modify the mutable state by invoking Counter.increment(). Finally, a new call to Counter.get shows that the modification took place, as the value is now 1.

While Counter.counter changes over time, Counter.label is immutable, always remaining the same. Therefore, assert(Counter.label == “CounterLabel”) will succeed both times.

Since Counter contains a mutable state, race conditions may happen if multiple threads use it concurrently.

Lastly, notice how, in the example above, we only need to reference the name of the singleton, Counter, without explicitly instantiating it.

4. Conclusion

In this brief tutorial, we saw how to define a singleton object with state and methods in Scala. We also experimented with it, learning how to use it and how, and when Scala instantiates it.

As usual, the full source code can be found over on GitHub.