When to use an abstract class in Scala (instead of a trait)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 4.12, “When to use an abstract class in Scala.”

Problem

Scala has traits, and a trait is more flexible than an abstract class, so you wonder, “When should I use an abstract class?”

Solution

There are two main reasons to use an abstract class in Scala:

  • You want to create a base class that requires constructor arguments.
  • The code will be called from Java code.

Regarding the first reason, traits don’t allow constructor parameters:

// this won't compile
trait Animal(name: String)

So, use an abstract class whenever a base behavior must have constructor parameters:

abstract class Animal(name: String)

Regarding the second reason, if you’re writing code that needs to be accessed from Java, you’ll find that Scala traits with implemented methods can’t be called from Java code. If you run into this situation, see Recipe 17.7, “Wrapping Traits with Implementations”, for solutions to that problem.

Discussion

Use an abstract class instead of a trait when the base functionality must take constructor parameters. However, be aware that a class can extend only one abstract class.

Abstract classes work just like Java in that you can define some methods that have complete implementations, and other methods that have no implementation and are therefore abstract. To declare that a method is abstract, just leave the body of the method undefined:

def speak   // no body makes the method abstract

There is no need for an abstract keyword; simply leaving the body of the method undefined makes it abstract. This is consistent with how abstract methods in traits are defined.

In the following example the methods save, update, and delete are defined in the abstract class BaseController, but the methods connect, getStatus, and setServerName have no method body, and are therefore abstract:

abstract class BaseController(db: Database) {
    def save { db.save }
    def update { db.update }
    def delete { db.delete }

    // abstract
    def connect

    // an abstract method that returns a String
    def getStatus: String

    // an abstract method that takes a parameter
    def setServerName(serverName: String)
}

When a class extends the BaseController class, it must implement the connect, getStatus, and setServerName methods, or be declared abstract. Attempting to extend BaseController without implementing those methods yields a “class needs to be abstract” error, as shown in the REPL:

scala> class WidgetController(db: Database) extends BaseController(db)

<console>:9: error: class WidgetController needs to be abstract, since:
method setServerName in class BaseController of type (serverName: String)Unit is not defined
method getStatus in class BaseController of type => String is not defined
method connect in class BaseController of type => Unit is not defined
       class WidgetController(db: Database) extends BaseController(db)
             ^

Because a class can extend only one abstract class, when you’re trying to decide whether to use a trait or abstract class, always use a trait, unless you have this specific need to have constructor arguments in your base implementation.