The Practical Difference between Abstract Classes and Traits in Scala
Overview
This post will be going through the practicalities that come with Abstract Classes and Traits in Scala.
We will be comparing the both, when and where to use Abstract Classes or Trait.
Differences
Traits
- Traits support multiple inheritance
- An object instance can have a trait added to it.
- Do not contain constructor parameters.
- Completely inter-operable only when they do not contain any implementation code
- Super calls are dynamically bound so it is stackable
Abstract Classes
- Abstract class does not support multiple inheritance
- An object instance cannot have an abstract class added to it.
- Contain constructor parameters
- Completely inter-operable with Java code
- Super calls are statically bound so it is not stackable
Which one to use?
Whenever you implement a reusable collection of behavior, you will have to decide whether you want to use a trait or an abstract class. There is no firm rule, but this section contains a few guidelines to consider.
Developer Thoughts…
Make it a concrete class if the behavior will not be reused. 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 , use an abstract class. Since traits with code do not have a close Java analog, it tends to be awkward to inherit from a trait in a Java class. Inheriting from a Scala class, meanwhile, is exactly like inheriting from a Java class. As one exception, a Scala trait with only abstract members translates directly to a Java interface, so you should define such traits even if you expect Java code to inherit from it.
If you plan to distribute it in compiled form, and you expect outside groups to write classes inheriting from it, you might lean towards using an abstract class. The issue is that when a trait gains or loses a member, any classes that inherit from it must be recompiled, even if they have not changed. If outside clients will only call into the behavior, instead of inheriting from it, then use trait.
If efficiency is very important, go for a class. Most Java runtimes make a virtual method invocation of a class member a faster operation than an interface method invocation. Traits gets compiled to interfaces and therefore may pay a slight performance overhead. However, you should make this choice only if you know that the trait in question constitutes a performance bottleneck and have evidence that using a class instead actually solves the problem.
If you still do not know, after considering the above, then start by making it as a trait. You can always change it later, and in general using a trait keeps more options open.
The Code
Regarding the first reason, Scala traits don’t allow constructor parameters:
//This wont compiletrait Animal(name: String)
Therefore, you need to use an abstract class whenever a base behavior must have constructor parameters:
abstract class Animal(name: String)
Trait
To demonstrate this, here’s a Scala trait that has a concrete method named speak, and an abstract method named comeToMaster:
trait Pet {def speak = println(“Yo”) // concrete implementation of a speak methoddef comeToMaster(): Unit // abstract}
When a class extends a trait, each defined method must be implemented, so here’s a class that extends Pet and defines comeToMaster:
class Dog(name: String) extends Pet {def comeToMaster(): Unit = println(“Woo-hoo, I’m coming!”)}val d = new Dog(“Zeus”)
Then you can call speak and comeToMaster. This is what it looks like in the REPL:
scala> val d = new Dog(“duffy”)d: Dog = Dog@4136cb25scala> d.speakYoscala> d.comeToMasterWoo-hoo, I’m coming!
Abstract Class
The abstract class syntax is similar to the trait syntax. For example, here’s an abstract class named Pet that’s similar to the Pet Trait we defined earlier:
abstract class Pet (name: String) {def speak(): Unit = println(“Yo”) // concrete implementationdef comeToMaster(): Unit // abstract method}Given that abstract Pet class, you can define a Dog class like this:class Dog(name: String) extends Pet(name) {override def speak() = println(“Woof”)def comeToMaster() = println(“Here I come!”)}
The REPL shows that this all works as advertised:
scala> val d = new Dog(“duffy”)
d: Dog = Dog@51f1fe1cscala> d.speakWoofscala> d.comeToMasterHere I come!
Conclusion
For whatever it is worth, Odersky’s “Programming in Scala” recommends that, when you doubt, you use traits. You can always change them into abstract classes later on if needed.