Scala DISCUSSION

Scala class vs object vs case class: what does each one give me, and when should I use which?

Started by ts Scala case classsingleton objectcompanion objectapply methodstructural equality
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Scala class vs object vs case class: what does each one give me, and when should I use which?

ts Scala Forum
#1

Coming from Java, I am unsure about Scala's three ways of defining things. I see class Sensor(id: Int), object Config and case class Reading(sensorId: Int, value: Double) in the same project, and sometimes a class and an object with the same name in one file. Reading(1, 2.5) == Reading(1, 2.5) is true, but two new Sensor(1) instances are not equal.

What does each construct provide, and how do I decide between them?

Community replies 5

Re: Scala class vs object vs case class: what does each one give me, and when should I use which?

#2

A class is a blueprint, as in Java: each instance has its own state. An object is a single instance defined together with its own anonymous class. It is created lazily the first time it is used and there is never a second one. Scala has no static keyword; anything you would make static in Java (constants, factory methods, utility functions, the main entry point) goes into an object.

Note that in class Sensor(id: Int) the parameter is only a constructor argument. It becomes a public field only if you write val id: Int.

Re: Scala class vs object vs case class: what does each one give me, and when should I use which?

#3

An object with the same name as a class, in the same file, is that class's companion. The two can see each other's private members, and the companion is where factory methods live. A method named apply is called when you use the object like a function, so with object Sensor { def apply(id: Int): Sensor = new Sensor(id) } you can write Sensor(7) without new.

The counterpart is unapply, which the compiler calls during pattern matching to take a value apart. Scala 3 additionally lets you omit new for any class, but the companion is still the home of alternative constructors and constants.

Re: Scala class vs object vs case class: what does each one give me, and when should I use which?

#4

A case class is a regular class for which the compiler generates the code you would otherwise write by hand for a data carrier: every constructor parameter becomes a public val; equals and hashCode compare the field values; toString prints Reading(1,2.5); a companion object with apply and unapply appears; and a copy method creates a modified duplicate, as in r.copy(value = 3.0).

That is why your two Reading values are equal: they are compared field by field. An ordinary class inherits reference equality, so two separately created Sensor objects are different even with the same id, unless you override equals and hashCode yourself.

Re: Scala class vs object vs case class: what does each one give me, and when should I use which?

#5

A reasonable division of labour: case classes for immutable data (messages, records, configuration, results), pattern-matched with case Reading(id, v) if v > 100 => ...; plain classes for things with identity, mutable state or resources, such as a serial-port connection or a service with dependencies; objects for stateless helpers and for single values in a hierarchy (case object Timeout).

Keep case-class fields immutable. If a field is a var or a mutable collection, the hash code changes when it is modified, and an instance used as a key in a HashMap or stored in a HashSet can no longer be found.

Re: Scala class vs object vs case class: what does each one give me, and when should I use which?

#6

Two limitations to know. A case class cannot extend another case class; the generated equality would be ambiguous. Put shared members in a trait or an abstract class and let the case classes be the leaves, ideally under a sealed trait so the compiler can check that a match covers every case.

And generated equality compares only the fields of the first parameter list. In case class Reading(sensorId: Int, value: Double)(val note: String), two readings that differ only in note are equal, which is occasionally useful and occasionally a surprise.

TEP COMMUNITY