Unlike class inheritance, in which each class must inherit from just one superclass, a class can mix in any number of traits. Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters.
Using a trait like a Java interface
A trait can be used just like a Java interface. As with interfaces, just define the methods in your trait that you want extending
classes to implement:
trait BaseSoundPlayer {
def play
def close
def pause
def stop
def resume
}
Extending traits
If a class implements one trait it will use the extends keyword:
class Mp3SoundPlayer extends BaseSoundPlayer {
def play {}
def close {}
def pause {}
def stop {}
def resume {}
}
One trait can extend another trait:
trait Mp3BaseSoundFilePlayer extends BaseSoundFilePlayer {
def getBasicPlayer:BasicPlayer
def getBasicController:BasicController
def setGain(volume: Double)
}
If a class extends a trait but does not implement the methods defined in that trait, it must be declared abstract:
// must be declared abstract because it does not implement BaseSoundPlayer methods
abstract class JavaSoundPlayer extends BaseSoundPlayer {
def play {}
def close {}
}
If a class implements multiple traits, it will extend the first trait (or a class, or abstract class), and then use with for other traits:
abstract class Animal {
def speak
}
trait WaggingTail {
def startTail
def stopTail
}
trait FourLeggedAnimal {
def walk
def run
}
class Dog extends Animal with WaggingTail with FourLeggedAnimal {
// implementation code here ...
}
Abstract and concrete fields in traits
In a trait, define a field with an initial value to make it concrete, otherwise give it no initial value to make it abstract:
trait PizzaTrait {
var numToppings: Int // abstract
val maxNumToppings = 10 // concrete
}
In the class that extends the trait, you’ll need to define the values for the abstract fields, or make the class abstract. The Pizza class demonstrates how to override the numToppings field:
class Pizza extends PizzaTrait {
var numToppings = 0 // override not needed
}
trait PizzaTrait {
var numToppings: Int // abstract
val maxNumToppings = 10 // concrete
}
Using a trait like a Java abstract class
You can use traits like abstract classes in Java. In the following example, an implementation is provided for the speak method in the Pet trait, so implementing classes don’t have to override it. The Dog class chooses not to override it, while the Cat class does:
trait Pet {
def speak { println("Yo") } // concrete implementation of a speak method
def comeToMaster // abstract
}
class Dog extends Pet {
// don't need to implement 'speak' if you don't want to
def comeToMaster { ("I'm coming!") }
}
class Cat extends Pet {
// override 'speak'
override def speak { ("meow") }
def comeToMaster { ("That's not gonna happen.") }
}
If a class extends a trait without implementing its abstract methods, it must be defined abstract. Because FlyingPet does not implement comeToMaster, it is defined abstract:
abstract class FlyingPet extends Pet {
def fly { ("I'm flying!") }
}
Using traits as simple mixins
To implement a simple mixin, define the methods you want in your trait, then add the trait to your class using extends or with. In the following example we define a Tail trait:
trait Tail {
def wagTail { println("tail is wagging") }
}
This trait can be used with an abstract Pet class to create a Dog:
abstract class Pet (var name: String) {
def speak // abstract
def ownerIsHome { println("excited") }
}
class Dog (name: String) extends Pet (name) with Tail {
def speak { println("woof") }
}
A Dog can now use the methods defined by both the abstract Pet class, as well as the Tail trait:
object Test extends App {
val zeus = new Dog("Zeus")
zeus.ownerIsHome
zeus.wagTail
zeus.speak
}
Limiting which classes can use a trait
You can limit a trait so it can only be added to classes which extend a specific subclass. To do this, use the “trait [TraitName] extends [SubclassName]” syntax. For instance, in the following example the Starship and WarpCore both extend the common superclass StarfleetComponent, so the WarpCore trait can be mixed into the Starship class:
class StarfleetComponent
trait WarpCore extends StarfleetComponent
class Starship extends StarfleetComponent with WarpCore
However, in the following example, the Warbird can’t extend the WarpCore trait because Warbird and WarpCore don’t share the same superclass:
class StarfleetComponent
trait WarpCore extends StarfleetComponent
class RomulanStuff
class Warbird extends RomulanStuff with WarpCore // won't compile
A trait inheriting a class is not a common occurrence, and in general the following approach is preferred.
You can mark your traits so they can only be used by subclasses of a certain type. To do this, begin your trait with the “this: LimitingType =>” statement, as shown here:
trait WarpCore {
this: Starship =>
}
This next example shows that you can add a trait to an instance of a class (an object):
class DavidBanner
trait Angry {
println("You won't like me ...")
}
object Test extends App {
val hulk = new DavidBanner with Angry
}
Extending Java interfaces like Scala traits
You can extend Java interfaces like Scala traits. In your Scala application, just use the extends and with keywords to implement your Java interfaces:
// java
public interface Animal {
public void speak();
}
public interface Wagging {
public void wag();
}
public interface Running {
public void run();
}
// scala
class Dog extends Animal with Wagging with Running {
def speak { println("Woof") }
def wag { println("Tail is wagging!") }
def run { println("I'm running!") }
}