Scala - Sealed Trait



Scala sealed traits allow you to restrict which classes can extend a trait. When a trait is marked as sealed, all of its subclasses must be defined in the same file as the sealed trait. This ensures that all possible subclasses are known at compile time, enabling exhaustive pattern matching and providing additional compile-time safety.

Defining a Sealed Trait

A sealed trait is defined just like a regular trait but uses the sealed keyword. Below is the syntax to define a sealed trait −

sealed trait Shape

case class Circle(radius: Double) extends Shape
case class Rectangle(length: Double, breadth: Double) extends Shape
case class Square(side: Double) extends Shape

Example of a Sealed Trait

In this example, we define a sealed trait Shape and three case classes Circle, Rectangle, and Square that extend Shape. All subclasses must be in the same file.

sealed trait Shape

case class Circle(radius: Double) extends Shape
case class Rectangle(length: Double, breadth: Double) extends Shape
case class Square(side: Double) extends Shape

object Demo {
   def describe(shape: Shape): String = shape match {
      case Circle(radius) => s"A circle with radius $radius"
      case Rectangle(length, breadth) => s"A rectangle with length $length and breadth $breadth"
      case Square(side) => s"A square with side $side"
   }

   def main(args: Array[String]): Unit = {
      val shapes: List[Shape] = List(Circle(5.0), Rectangle(2.0, 3.0), Square(4.0))

      shapes.foreach { shape =>
         println(describe(shape))
      }
   }
}

Save the above program in Demo.scala. The following commands are used to compile and execute this program.

Command

\>scalac Demo.scala
\>scala Demo

The output will be −

A circle with radius 5.0
A rectangle with length 2.0 and breadth 3.0
A square with side 4.0 

Usage of Sealed Traits

Sealed traits are particularly useful for defining closed hierarchies where all possible subclasses are known. This allows the compiler to check for exhaustive pattern matching, improving code safety and reliability.

Example

sealed trait Status

case object Success extends Status
case class Failure(reason: String) extends Status

object Demo {
   def getStatusMessage(status: Status): String = status match {
      case Success => "Operation was successful"
      case Failure(reason) => s"Operation failed: $reason"
   }

   def main(args: Array[String]): Unit = {
      val statuses: List[Status] = List(Success, Failure("Invalid input"))

      statuses.foreach { status =>
         println(getStatusMessage(status))
      }
   }
}

In this example, the sealed trait Status has two subclasses, Success and Failure. The pattern matching in getStatusMessage is exhaustive, covering all possible cases.

Save the above program in Demo.scala. The following commands are used to compile and execute this program.

Command

\>scalac Demo.scala
\>scala Demo

The output will be −

Operation was successful
Operation failed: Invalid input 

Advanced Example with Sealed Traits

To further illustrate the power of sealed traits, let's consider a more complex example involving nested sealed traits.

Example

sealed trait Animal

sealed trait Mammal extends Animal
case class Dog(name: String) extends Mammal
case class Cat(name: String) extends Mammal

sealed trait Bird extends Animal
case class Sparrow(name: String) extends Bird
case class Parrot(name: String) extends Bird

object Demo {
   def describe(animal: Animal): String = animal match {
      case Dog(name) => s"A dog named $name"
      case Cat(name) => s"A cat named $name"
      case Sparrow(name) => s"A sparrow named $name"
      case Parrot(name) => s"A parrot named $name"
   }

   def main(args: Array[String]): Unit = {
      val animals: List[Animal] = List(Dog("Buddy"), 
      Cat("Whiskers"), Sparrow("Jack"), Parrot("Polly"))

      animals.foreach { animal =>
         println(describe(animal))
      }
   }
}

Save the above program in Demo.scala. The following commands are used to compile and execute this program.

Command

\>scalac Demo.scala
\>scala Demo

The output will be −

A dog named Buddy
A cat named Whiskers
A sparrow named Jack
A parrot named Polly 

Sealed Trait Summary

  • Traits are used to compose behavior in Scala. Sealed traits allow the compiler to ensure that pattern matching is exhaustive and to cover all possible subclasses.
  • Sealed traits provide additional compile-time safety and make the code easier to reason about by restricting the subclasses to the same file
  • Sealed traits are ideal for defining closed hierarchies where all possible subclasses are known and fixed.
  • You can use sealed traits to improve code readability and maintainability by making it clear which classes belong to a particular hierarchy.
  • While sealed traits restrict the addition of new subclasses, these can still be extended by adding new case classes or objects within the same file.
Advertisements