Scala Object-Oriented Programming

Scala is a hybrid language — everything is an object, and everything is a function. This chapter covers its OOP features in depth.

Classes and Constructors

Scala
class BankAccount(val owner: String, var balance: Double = 0.0) {
  // Primary constructor is the class body itself
  
  def deposit(amount: Double): Unit = {
    require(amount > 0, "Deposit must be positive")
    balance += amount
  }
  
  def withdraw(amount: Double): Either[String, Double] = {
    if (amount > balance) Left("Insufficient funds")
    else {
      balance -= amount
      Right(balance)
    }
  }
  
  override def toString: String = s"Account($owner: $$${balance})"
}

val account = new BankAccount("Alice", 1000.0)
account.deposit(500)
account.withdraw(200)  // Right(1300.0)
account.withdraw(5000) // Left("Insufficient funds")

Inheritance

Scala
abstract class Shape {
  def area: Double
  def perimeter: Double
  def describe: String = f"${getClass.getSimpleName}(area=$area%.2f, perimeter=$perimeter%.2f)"
}

class Circle(val radius: Double) extends Shape {
  def area: Double = math.Pi * radius * radius
  def perimeter: Double = 2 * math.Pi * radius
}

class Rectangle(val width: Double, val height: Double) extends Shape {
  def area: Double = width * height
  def perimeter: Double = 2 * (width + height)
}

class Square(side: Double) extends Rectangle(side, side)

val shapes: List[Shape] = List(Circle(5), Rectangle(4, 6), Square(3))
shapes.foreach(s => println(s.describe))
// Circle(area=78.54, perimeter=31.42)
// Rectangle(area=24.00, perimeter=20.00)
// Square(area=9.00, perimeter=12.00)

Traits

Scala
// Traits = interfaces + concrete methods + fields
trait Logger {
  def log(message: String): Unit = println(s"[LOG] $message")
  def logError(message: String): Unit = log(s"ERROR: $message")
}

trait Repository[T] {
  def findAll(): List[T]
  def findById(id: Long): Option[T]
  def save(entity: T): T
  def delete(id: Long): Boolean
}

trait Cacheable {
  private var cache = Map.empty[String, Any]
  
  def getCached(key: String): Option[Any] = cache.get(key)
  def setCache(key: String, value: Any): Unit = cache += (key -> value)
  def clearCache(): Unit = cache = Map.empty
}

// Mixing traits
class UserRepository extends Repository[User] with Logger with Cacheable {
  def findAll(): List[User] = {
    log("Fetching all users")
    // implementation...
    List.empty
  }
  // ...
}

Companion Objects

Scala
// Singleton object — shared state/methods (like Java static)
class User private (val name: String, val email: String)

object User {
  // Factory method
  def apply(email: String): User = {
    val name = email.split("@").head
    new User(name, email)
  }
  
  def fromDatabase(id: Long): Option[User] = {
    // Database lookup...
    Some(new User("Alice", "alice@example.com"))
  }
  
  implicit val ordering: Ordering[User] = Ordering.by(_.name)
}

val user = User("alice@example.com")  // Uses companion object apply

Case Classes (Deep Dive)

Scala
case class Point(x: Double, y: Double)
case class Line(start: Point, end: Point)

// Auto-generated:
// - equals/hashCode (structural equality)
// - toString
// - copy method
// - unapply (for pattern matching)
// - companion object with apply

val p1 = Point(1, 2)
val p2 = Point(1, 2)
p1 == p2                    // true (structural equality)
p1 == Point(3, 4)           // false

// Copy with modifications
val p3 = p1.copy(x = 10)   // Point(10, 2)

// Pattern matching with case classes
def midpoint(line: Line): Point = line match {
  case Line(Point(x1, y1), Point(x2, y2)) => 
    Point((x1 + x2) / 2, (y1 + y2) / 2)
}

Sealed Classes and Enums

Scala
// Sealed — all subclasses must be in the same file
sealed trait PaymentMethod
case class CreditCard(number: String, expiry: String) extends PaymentMethod
case class BankTransfer(account: String, routing: String) extends PaymentMethod
case object Cash extends PaymentMethod

def processPayment(method: PaymentMethod, amount: Double): String = method match {
  case CreditCard(number, _) => s"Charged $$${amount} to card ending ${number.takeRight(4)}"
  case BankTransfer(account, _) => s"Transferred $$${amount} to account $account"
  case Cash => s"Received $$${amount} in cash"
}

// Scala 3 enum
enum Color(val hex: String):
  case Red extends Color("#ff0000")
  case Green extends Color("#00ff00")
  case Blue extends Color("#0000ff")
  
  def name: String = toString.toLowerCase

Color.Red.hex  // "#ff0000"

💡 Tip: Use traits for capability/behavior, case classes for data, sealed traits for closed hierarchies, and abstract classes when you need constructors.

Next: Functional Programming in Scala