One thing I hear from people new to Scala is confusion around traits. How are they different than an abstract class, what are some real-world use cases for traits, and how do you know when to use a trait versus an abstract class, etc. These are all questions that come up quite a bit with new Scala developers. So, I thought it would be a good idea to write a series of articles around traits.
In this first article on traits, I'm going to discuss two topics regarding traits:
- What is the motivation behind traits and why do we need them?
- How can we use traits to accomplish what C++ gives us with multiple inheritance?
Let's start by talking about why traits are needed. Most of us Java programmers spent some time writing OOP code in C++. In C++, we had multiple inheritance. As powerful as it seems, multiple inheritance can lead to a ton of problems in the world of OOP. For example, what if we are designing an inventory system for an automotive dealership that specializes in selling both personal vehicles and work vehicles. We decide to create a class called "WorkVehicle" and "PersonalVehicle". Both classes inherit from a base class called "Vehicle". But along comes a new vehicle to the market that is both a personal vehicle and a work vehicle. So, we create a new class called "HybridVehicle" that extends both WorkVehicle and PersonalVehicle. The end-result is a messy class diagram that illustrates the dreaded diamond inheritance relationship. We now have to code around the merging of common fields.
Java decided that this is enough of a problem, that it's excluded from the language altogether. You're just now allowed to extend multiple classes. However, this scenario crops-up fairly often in software development, where we need to model entities that share characteristics with other entities in the system, but a parent-child relationship isn't the appropriate fit. Scala's answer to this problem is
traits. A Scala trait can have a combination of abstract methods and methods with default implementation.
Let's look at a simple example of how the vehicle problem might be solved using a trait. In the code example below, we have an abstract class named Vehicle. We've extended Vehicle to create a PersonalVehicle class. We've realized that the thing that makes a WorkVehicle a "vehicle capable of doing work" is that it has a weight that it can safely tow/haul, so we've created a trait called WorkVehicle. This trait has a method with a default implementation, which can be overridden if you need. Notice the use of the
with keyword when we mix-in a trait.
abstract class Vehicle
class PersonalVehicle extends Vehicle
trait WorkVehicle {
def towWeight: Integer = {
return 2500
}
}
class HybridVehicle extends PersonalVehicle with WorkVehicle
This should illustrate how easy it is in Scala, to model various
characteristics of objects using traits and mix-in these traits where we
need. The result is a clean relationship that you don't get with
multiple inheritance in C++, yet we still have the same power that we
like out of multiple inheritance, which Java doesn't provide.