# Classes, Objects and Companions — Scala

Source: https://www.geekswithgeeks.com/en/scala/o-classes

> Define classes, singleton objects and companion objects.

## Object-oriented building blocks

A **class** declares its primary constructor in the header: `class Account(val owner: String, private var balance: BigDecimal)`. Parameters marked `val` become public read-only fields, `var` makes them mutable, and unmarked ones are private to the class. In Scala 3, `new` is optional: `Account("Asha", 0)` works (universal apply). Methods are public by default, with `private` and `protected` modifiers available. An **`object`** is a **singleton**: a single instance created lazily on first use, used for utilities, constants and entry points. An object with the same name as a class in the same file is its **companion object**; class and companion can access each other's private members, and the companion is the place for **factory methods** (`apply`), constants and validation (`def fromString(s: String): Option[Account]`). Override `toString`, `equals` and `hashCode` when needed, though **case classes** (next topics) generate them for you. Use `require(cond, msg)` to validate constructor arguments.

## Class and companion object

The class describes instances; the companion object holds factories and shared members.

![Two connected boxes side by side, one showing several small instance copies and the other a single star-marked singleton.](assets/figures/scala/section-3-map.svg) — Figure 3.1 — A class with its companion object.

## A class with a companion factory

Validation, private state and a smart constructor.

```scala
class Account private (val owner: String, private var balancePaise: Long):
  require(owner.nonEmpty, "owner is required")

  def balance: Long = balancePaise

  def deposit(amountPaise: Long): Unit =
    require(amountPaise > 0, "amount must be positive")
    balancePaise += amountPaise

  def withdraw(amountPaise: Long): Either[String, Long] =
    if amountPaise > balancePaise then Left(s"Insufficient funds for $owner")
    else
      balancePaise -= amountPaise
      Right(balancePaise)

  override def toString = s"Account($owner, $balancePaise paise)"

object Account:
  val MinimumOpeningPaise = 50000L

  def open(owner: String, initialPaise: Long): Either[String, Account] =
    if initialPaise < MinimumOpeningPaise then Left("Opening balance too low")
    else Right(new Account(owner, initialPaise))   // companion can call the private constructor

@main def bank(): Unit =
  Account.open("Asha", 100000) match
    case Right(acc) => println(acc.withdraw(30000))  // Right(70000)
    case Left(err)  => println(err)
```

## Smart constructors

Making the constructor `private` and exposing a companion method that returns `Either` or `Option` guarantees that every `Account` in your program is valid, without throwing exceptions.

**Quiz:** What is a companion object?

- [x] An object with the same name as a class in the same file, which can access its private members
- [ ] A subclass
- [ ] A copy of every instance
- [ ] A Java interface

*Answer:* An object with the same name as a class in the same file, which can access its private members. Companions share privacy with their class and typically hold factories and constants.
