Scala: How to limit which classes can use a trait by inheritance

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 8.5, “How to limit which Scala classes can use a trait by inheritance.”

Problem

You want to limit a Scala trait so it can only be added to classes that extend a superclass or another trait.

Solution

Use the following syntax to declare a trait named TraitName, where TraitName can only be mixed into classes that extend a type named SuperThing, where SuperThing may be a trait, class, or abstract class:

trait [TraitName] extends [SuperThing]

For instance, in the following example, Starship and StarfleetWarpCore both extend the common superclass StarfleetComponent, so the StarfleetWarpCore trait can be mixed into the Starship class:

class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class Starship extends StarfleetComponent with StarfleetWarpCore

However, in the following example, the Warbird class can’t extend the StarfleetWarpCore trait, because Warbird and StarfleetWarpCore don’t share the same superclass:

class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class RomulanStuff

// won't compile
class Warbird extends RomulanStuff with StarfleetWarpCore

Attempting to compile this second example yields this error:

error: illegal inheritance; superclass RomulanStuff
   is not a subclass of the superclass StarfleetComponent
   of the mixin trait StarfleetWarpCore
class Warbird extends RomulanStuff with StarfleetWarpCore
                                        ^

Discussion

A trait inheriting from a class is not a common occurrence, and in general, Recipes 8.6 and Recipe 8.7 are more commonly used to limit the classes a trait can be mixed into. However, when this situation occurs, you can see how inheritance can be used. As long as a class and a trait share the same superclass — Starship and StarfleetWarpCore extend StarfleetComponent — the code will compile, but if the superclasses are different (Warbird and StarfleetWarpCore have different superclasses), the code will not compile.

As a second example, in modeling a large pizza store chain that has a corporate office and many small retail stores, the legal department creates a rule that people who deliver pizzas to customers must be a subclass of StoreEmployee and cannot be a subclass of CorporateEmployee. To enforce this, begin by defining your base classes:

abstract class Employee
class CorporateEmployee extends Employee
class StoreEmployee extends Employee

Someone who delivers food can only be a StoreEmployee, so you enforce this requirement in the DeliversFood trait using inheritance like this:

trait DeliversFood extends StoreEmployee

Now you can define a DeliveryPerson class like this:

// this is allowed
class DeliveryPerson extends StoreEmployee with DeliversFood

Because the DeliversFood trait can only be mixed into classes that extend StoreEmployee, the following line of code won’t compile:

// won't compile
class Receptionist extends CorporateEmployee with DeliversFood

Discussion

It seems rare that a trait and a class the trait will be mixed into should both have the same superclass, so I suspect the need for this recipe is also rare. When you want to limit the classes a trait can be mixed into, don’t create an artificial inheritance tree to use this recipe; use one of the following recipes instead.

See Also