How to use a Scala trait as an interface (like a Java interface)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 8.1, “How to use a Scala trait as an interface.”

Problem

You’re used to creating interfaces in other languages like Java, and want to create something like that in Scala.

Solution

You can use a Scala trait just like a Java interface. As with interfaces, just declare the methods in your trait that you want extending classes to implement:

trait BaseSoundPlayer {
    def play
    def close
    def pause
    def stop
    def resume
}

If the methods don’t take any argument, you only need to declare the names of the methods after the def keyword, as shown. If a method should require parameters, list them as usual:

trait Dog {
    def speak(whatToSay: String)
    def wagTail(enabled: Boolean)
}

When a class extends a trait, it uses the extends and with keywords. When extending one trait, use extends:

class Mp3SoundPlayer extends BaseSoundPlayer { ...

When extending a class and one or more traits, use extends for the class, and with for subsequent traits:

class Foo extends BaseClass with Trait1 with Trait2 { ...

When a class extends multiple traits, use extends for the first trait, and with for subsequent traits:

class Foo extends Trait1 with Trait2 with Trait3 with Trait4 { ...

Unless the class implementing a trait is abstract, it must implement all of the abstract trait methods:

class Mp3SoundPlayer extends BaseSoundPlayer {
    def play   { // code here ... }
    def close  { // code here ... }
    def pause  { // code here ... }
    def stop   { // code here ... }
    def resume { // code here ... }
}

If a class extends a trait but does not implement the abstract methods defined in that trait, it must be declared abstract:

// must be declared abstract because it does not implement
// all of the BaseSoundPlayer methods
abstract class SimpleSoundPlayer extends BaseSoundPlayer {
    def play  { ... }
    def close { ... }
}

In other uses, one trait can extend another trait:

trait Mp3BaseSoundFilePlayer extends BaseSoundFilePlayer {
    def getBasicPlayer: BasicPlayer
    def getBasicController: BasicController
    def setGain(volume: Double)
}

Discussion

As demonstrated, at their most basic level, traits can be used just like Java interfaces. In your trait, just declare the methods that need to be implemented by classes that want to extend your trait.

Classes extend your trait using either the extends or with keywords, according to these simple rules:

  • If a class extends one trait, use the extends keyword.
  • If a class extends multiple traits, use extends for the first trait and with to extend (mix in) the other traits.
  • If a class extends a class (or abstract class) and a trait, always use extends before the class name, and use with before the trait name(s).

You can also use fields in your traits. See the next recipe for examples.

As shown in the WaggingTail trait in the following example, not only can a trait be used like a Java interface, but it can also provide method implementations, like an abstract class in Java:

abstract class Animal {
    def speak(): Unit
}

trait WaggingTail {
    def startTail() = println("tail started")
    def stopTail() = println("tail stopped")
}

trait FourLeggedAnimal {
    def walk(): Unit
    def run(): Unit
}

class Dog extends Animal with WaggingTail with FourLeggedAnimal {
    // implementation code here ...
    def speak() = println("Dog says 'woof'")
    def walk() = println("Dog is walking")
    def run() = println("Dog is running")
}

This ability is discussed in detail in Recipe 8.3, “Using a Trait Like an Abstract Class”.

When a class has multiple traits, such as the WaggingTail and FourLeggedAnimal traits in this example, those traits are said to be mixed into the class. The term “mixed in” is also used when extending a single object instance with a trait, like this:

val f = new Foo with Trait1

This feature is discussed more in Recipe 8.8, “Adding a Trait to an Object Instance”.