This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 8.6, “How to define a Scala trait so it can only be subclassed by a certain type.”
Problem
You want to mark a Scala trait so it can only be used by types that extend a certain base type.
Solution
To make sure that a trait named MyTrait
can only be mixed into a class that is a subclass of a type named BaseType
, begin your trait with a this: BaseType =>
declaration, as shown here:
trait MyTrait { this: BaseType =>
For instance, to make sure a StarfleetWarpCore
can only be used in a Starship
, mark the StarfleetWarpCore
trait like this:
trait StarfleetWarpCore { this: Starship => // more code here ... }
Given that declaration, this code will work:
class Starship class Enterprise extends Starship with StarfleetWarpCore
But other attempts like this will fail:
class RomulanShip // this won't compile class Warbird extends RomulanShip with StarfleetWarpCore
This second example fails with an error message similar to this:
error: illegal inheritance; self-type Warbird does not conform to StarfleetWarpCore's selftype ↵ StarfleetWarpCore with Starship class Warbird extends RomulanShip with StarfleetWarpCore ^
Discussion
As shown in the error message, this approach is referred to as a Scala self type. The Scala Glossary includes this statement as part of its description of a self type:
“Any concrete class that mixes in the trait must ensure that its type conforms to the trait’s self type.”
A trait can also require that any type that wishes to extend it must extend multiple other types. The following WarpCore
definition requires that any type that wishes to mix it in must extend WarpCoreEjector
and FireExtinguisher
, in addition to extending Starship
:
trait WarpCore { this: Starship with WarpCoreEjector with FireExtinguisher => }
Because the following Enterprise
definition matches that signature, this code compiles:
class Starship trait WarpCoreEjector trait FireExtinguisher // this works class Enterprise extends Starship with WarpCore with WarpCoreEjector with FireExtinguisher
But if the Enterprise
doesn’t extend Starship
, WarpCoreEjector
, and FireExtinguisher
, the code won’t compile. Once again, the compiler shows that the self-type signature is not correct:
// won't compile class Enterprise extends Starship with WarpCore with WarpCoreEjector error: illegal inheritance; self-type Enterprise does not conform to WarpCore's selftype WarpCore with Starship with WarpCoreEjector with FireExtinguisher class Enterprise extends Starship with WarpCore with WarpCoreEjector ^
See Also
- Recipe 8.5 shows how to limit which classes can use a Scala trait by declaring inheritance
- Recipe 8.7 demonstrates the technique to use to make sure a Scala trait can only be mixed into classes that have a specific method
- The Scala Glossary
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |