Traits are a fundamental unit of code reuse in Scala. A trait encapsulates method and field definitions, which can then be reused by mixing them into classes. Unlike class inheritance, in which each class must inherit from just one superclass, a class can mix in any number of traits.
- Once a trait is defined, it can be mixed in to a class using either the extends or with keywords
- If you wish to mix a trait into a class that explicitly extends a superclass, you use extends to indicate the superclass and with to mix in the trait. If you want to mix in multiple traits, you add more with clauses.
- Traits can declare fields and maintain state. In fact, you can do anything in a trait definition that you can do in a class definition, and the syntax looks exactly the same, with only two exceptions. First, a trait cannot have any “class” parameters, i.e., parameters passed to the primary constructor of a class. Second difference is that whereas in classes, super calls are statically bound, in traits, they are dynamically bound. If you write “super.toString” in a class, you know exactly which method implementation will be invoked. When you write the same thing in a trait, however, the method implementation to invoke for the super call is undefined when you define the trait. Rather, the implementation to invoke will be determined anew each time the trait is mixed into a concrete class. This curious behaviour of super is key to allowing traits to work as stackable modifications
- One major use of traits is to automatically add methods to a class in terms of methods the class already has. That is, traits can enrich a thin interface, making it into a rich interface
- To enrich an interface using traits, simply define a trait with a small number of abstract methods—the thin part of the trait’s interface—and a potentially large number of concrete methods, all implemented in terms of the abstract methods. Then you can mix the enrichment trait into a class, implement the thin portion of the interface, and end up with a class that has all of the rich interface available
- Traits let you modify the methods of a class, and they do so in a way that allows you to stack those modifications with each other
To trait, or not to trait?
- If the behavior will not be reused, then make it a concrete class. It is not reusable behavior after all
- If it might be reused in multiple, unrelated classes, make it a trait. Only traits can be mixed into different parts of the class hierarchy
- If you want to inherit from it in Java code, use an abstract class
- If efficiency is very important, lean towards using a class. Most Java runtimes make a virtual method invocation of a class member a faster operation than an interface method invocation. Traits get compiled to interfaces and therefore may pay a slight performance overhead