BudiBadu Logo
Samplebadu

Scala by Example: Traits

Scala 3

Implementing code reuse through mixin composition with this code example showing trait definitions with concrete and abstract methods, multiple trait mixing using with keyword, trait implementation requirements, and instance-level trait mixing for dynamic behavior modification.

Code

trait Greeter {
  def greet(name: String): Unit = 
    println(s"Hello, $name!")
}

trait Logger {
  def log(msg: String): Unit
}

// Class extending multiple traits
class Service extends Greeter with Logger {
  override def log(msg: String): Unit = {
    println(s"[INFO] $msg")
  }
  
  def run(): Unit = {
    log("Starting service")
    greet("User")
  }
}

val s = Service()
s.run()

// Mixing traits into instances
val s2 = new Service with Logger {
    override def log(msg: String) = println(s"Custom: $msg")
}

Explanation

Traits are Scala's fundamental mechanism for code reuse and modularity, serving as interfaces that can contain both abstract method declarations and concrete implementations. Unlike Java interfaces before Java 8, Scala traits have always supported method implementations and fields. A class can extend exactly one class but mix in multiple traits using the with keyword, enabling flexible mixin composition without the diamond problem of traditional multiple inheritance.

Traits can define abstract methods that implementing classes must provide, as demonstrated by the log method in the Logger trait. They can also provide default implementations like the greet method in Greeter, allowing shared behavior across unrelated classes. This combination makes traits ideal for defining interfaces with common functionality, reducing code duplication while maintaining flexibility.

One powerful feature unique to Scala is the ability to mix traits into specific instances at creation time rather than at the class level. This allows you to modify the behavior of individual objects without changing the class definition or creating subclasses. This dynamic composition is particularly useful for testing, where you might mix in mock implementations, or for creating specialized object variants with different logging, caching, or validation strategies.

Key trait capabilities include:

  • Defining both abstract and concrete methods for flexible code reuse
  • Mixing multiple traits into a single class using with keyword
  • Instance-level trait mixing for dynamic behavior modification
  • Linearization to resolve method conflicts in predictable order
  • Accessing private members when used as companion traits

Code Breakdown

1-4
trait Greeter defines reusable behavior with concrete implementation.
11
extends Greeter with Logger mixes in two traits for composition.
12
Class must implement abstract log method from Logger trait.
26-28
new Service with Logger mixes trait into instance for object-specific behavior.