Traits
Prerequisites: Classes
A trait defines a set of methods that classes can share. Traits can have abstract methods (no body) and concrete ones.
trait Shape:
def name: String
def area: Double
def describe: String = f"$name with area $area%.2f"
case class Circle(radius: Double) extends Shape:
val name = "Circle"
def area: Double = math.Pi * radius * radius
case class Rectangle(width: Double, height: Double) extends Shape:
val name = "Rectangle"
def area: Double = width * height
@main def shapes(): Unit =
val shapes: List[Shape] = List(Circle(1), Rectangle(2, 3.5))
shapes.foreach(s => println(s.describe))
println(f"Total: ${shapes.map(_.area).sum}%.2f")
Circle with area 3.14
Rectangle with area 7.00
Total: 10.14
extends Shapemakes a class implement the trait; it must define the abstract members (name,area).describeis inherited as-is.- Code written against
Shapeworks with every implementation — polymorphism.
Mixing in several traits
A class extends one class but can mix in many traits with with (or
commas in Scala 3):
trait Greeter:
def greet(name: String): String = s"Hello, $name"
trait Logger:
def log(message: String): Unit = println(s"[LOG] $message")
class WelcomeService extends Greeter, Logger:
def welcome(name: String): String =
log(s"welcoming $name")
greet(name)
@main def mixins(): Unit =
println(WelcomeService().welcome("Ada"))
[LOG] welcoming Ada
Hello, Ada
Trait parameters and stacking
Scala 3 traits can take parameters, and traits can override each
other's methods, calling super to stack behavior:
trait Formatter:
def format(s: String): String = s
trait Trimmed extends Formatter:
override def format(s: String): String = super.format(s.trim)
trait Upper extends Formatter:
override def format(s: String): String = super.format(s.toUpperCase)
trait Bracketed(open: String, close: String) extends Formatter:
override def format(s: String): String = open + super.format(s) + close
class Pretty extends Bracketed("[", "]"), Upper, Trimmed
@main def stacking(): Unit =
println(Pretty().format(" scala "))
[SCALA]
Calls run from the rightmost trait to the left: Trimmed, then
Upper, then Bracketed. Stackable traits make it easy to compose small
behaviors.