Skip to content
Classes and Companion Objects
step 1/7

Reading — step 1 of 7

Learn

~2 min readClasses and Traits

Programming in Scala (Odersky) treats class definitions as the foundational building block. Scala's class syntax is concise — primary constructor in the header, properties auto-declared, inheritance via extends.

Class with primary constructor

class Person(val name: String, var age: Int) {
  def greet: String = s"Hi, I'm $name and I'm $age"
}

val ada = new Person("Ada", 36)
println(ada.greet)         // Hi, I'm Ada and I'm 36
ada.age = 37               // OK — var
// ada.name = "Linus"      // ERROR — val
  • val name: String — read-only public property + ctor parameter
  • var age: Int — mutable public property + ctor parameter
  • Without val/var, the parameter is just a constructor arg (not a property)
  • extends for inheritance, with for additional traits

Auxiliary constructors

class Rectangle(val width: Int, val height: Int) {
  def this(side: Int) = this(side, side)         // delegates to primary
}

val sq = new Rectangle(5)            // 5x5
val r = new Rectangle(4, 6)

Auxiliary constructors must call this(...) to delegate to the primary.

In modern Scala, default arguments + named parameters often replace auxiliary constructors:

class Rectangle(val width: Int = 1, val height: Int = 1)

val sq = new Rectangle(width = 5, height = 5)

Companion objects — Scala's static

Scala has no static keyword. Instead, you pair a class with an object of the same name in the same file — its companion object:

class User(val name: String, val email: String)

object User {
  // "static" members — factory methods, constants, etc.
  def fromEmail(email: String): User = new User(email.split("@")(0), email)
  val MAX_NAME_LENGTH = 50
}

val u = User.fromEmail("[email protected]")
println(User.MAX_NAME_LENGTH)

The companion object is a singleton. Members can access each other's privates.

Companion + apply = factory shortcut:

object Point {
  def apply(x: Double, y: Double): Point = new Point(x, y)
}

class Point(val x: Double, val y: Double)

val p = Point(3.0, 4.0)         // calls Point.apply, no `new` needed

For case class, Scala generates this factory automatically — that's why Point(3.0, 4.0) works without new.

Visibility

  • public (default) — visible everywhere
  • private — only this class
  • protected — this class and subclasses
  • private[package] — package-scoped (Scala-specific)

Common mistakes

  • Forgetting val/var on ctor params — without it, the parameter ISN'T a property; you can't access it later.
  • Confusing new Point(...) with Point(...) — first creates an instance directly; second goes through Point.apply.
  • Multiple inheritance via classes — Scala has single class inheritance + multiple traits. Classes use extends for one base; with for traits.
  • Reaching for class when case class fits — for value-equality data containers, case class is far less boilerplate.
  • Putting state in a companion object that should be per-instance — the companion is a singleton; its state is shared globally.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…