Scala Objects

In Scala, object creates a singleton — one single instance that exists for the entire lifetime of the program. You never use new with an object. The JVM creates it automatically the first time your code references it. Objects serve as utility holders, namespaces, entry points, and factory containers.

Defining a Singleton Object

object MathUtils:
  val PI = 3.141592653589793

  def square(n: Double): Double = n * n
  def cube(n: Double): Double = n * n * n
  def circleArea(r: Double): Double = PI * r * r

// No 'new' — access directly
println(MathUtils.PI)             // 3.141592653589793
println(MathUtils.square(5))      // 25.0
println(MathUtils.circleArea(7))  // 153.93...

object MathUtils
      │
      └── Single instance in memory, shared by all code
          Access: MathUtils.square(5)

Object as Application Entry Point

In Scala 2 (and still valid in Scala 3), the program entry point is a method called main inside an object. Scala 3 simplified this with @main, but the object approach works everywhere:

object App:
  def main(args: Array[String]): Unit =
    println("Application started")
    println(s"Arguments: ${args.mkString(", ")}")

Object as Namespace

object Config:
  val host     = "localhost"
  val port     = 5432
  val database = "myapp_db"
  val maxPool  = 10

object Messages:
  val welcome    = "Welcome to the app!"
  val notFound   = "Resource not found"
  val serverError = "An error occurred. Please try again."

println(Config.host)         // localhost
println(Messages.welcome)    // Welcome to the app!

Object State (Careful With This)

Because an object is a singleton, any var inside it acts as global mutable state. Use this sparingly:

object Counter:
  private var count = 0

  def increment(): Unit = count += 1
  def reset(): Unit = count = 0
  def value: Int = count

Counter.increment()
Counter.increment()
Counter.increment()
println(Counter.value)   // 3
Counter.reset()
println(Counter.value)   // 0

Object vs Class


Object                          Class
──────────────────────────────  ────────────────────────────────
One instance (singleton)        Many instances possible
No 'new' keyword                Uses 'new' to create instances
Access directly by object name  Access via instance reference
Good for utilities, constants   Good for modeling entities
Like Java's static methods      Like Java's instance methods

Extends and Traits in Objects

An object can extend a class or implement traits:

trait Greeter:
  def greet(name: String): String

object FormalGreeter extends Greeter:
  def greet(name: String): String = s"Good day, $name."

object CasualGreeter extends Greeter:
  def greet(name: String): String = s"Hey, $name!"

def welcome(greeter: Greeter, name: String): Unit =
  println(greeter.greet(name))

welcome(FormalGreeter, "Alice")   // Good day, Alice.
welcome(CasualGreeter, "Bob")     // Hey, Bob!

Practical: Logger Singleton

object Logger:
  private var logLevel = "INFO"

  def setLevel(level: String): Unit = logLevel = level

  def log(level: String, message: String): Unit =
    val priority = Map("DEBUG" -> 0, "INFO" -> 1, "WARN" -> 2, "ERROR" -> 3)
    val current  = priority.getOrElse(logLevel, 1)
    val incoming = priority.getOrElse(level, 1)
    if incoming >= current then
      println(s"[$level] $message")

  def info(msg: String): Unit  = log("INFO", msg)
  def warn(msg: String): Unit  = log("WARN", msg)
  def error(msg: String): Unit = log("ERROR", msg)
  def debug(msg: String): Unit = log("DEBUG", msg)

Logger.info("Server started on port 8080")
Logger.warn("Memory usage at 85%")
Logger.setLevel("WARN")
Logger.info("This won't print — below WARN level")
Logger.error("Disk full!")
// [INFO] Server started on port 8080
// [WARN] Memory usage at 85%
// [ERROR] Disk full!

Leave a Comment

Your email address will not be published. Required fields are marked *