Scala Traits

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.

Traits are similar to Java 8’s interfaces with concrete methods. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters.

Trait like a Java interface

A trait can be used just like a Java interface. As with interfaces, just define the methods in your trait that you want extending classes to implement

trait TestTrait {
def testMethod():String
}

class TestClass extends TestTrait {
override def testMethod():String = {
return “test”
}
}
Trait like a Java abstract class

You can use Scala traits like abstract classes in Java.

In the following example, an implementation is provided for the testMethod1() in the TestTrait, so implementing classes don’t have to override it.TestClass1 chooses not to override it, while the TestClass2 does.

trait TestTrait {
def testMethod1():String={
return “testTrait”
}
def testMethod2():String
}

class TestClass1 extends TestTrait {
override def testMethod2():String = {
return “testClass1”
}

}

class TestClass2 extends TestTrait {
override def testMethod1():String = {
return “testClass2”
}
override def testMethod2():String = {
return “testClass2”
}

}

Trait vs Abstract Class

  1. A class can mix-in with multiple traits but can extend only one abstract class.
  2. Abstract classes can have constructor parameters as well as type parameters. Traits can have only type parameters.
  3. Abstract classes are fully interoperable with Java. You can call them from Java code without any wrappers. Traits are fully interoperable only if they do not contain any implementation code.

How Traits Handle the famous Diamond Problem

Whichever trait is declared last wins the race

In the below example, Trait1 and Trait2 extends BaseTrait and provide their own implementation for dummyMethod.

TestClass1 extends Trait1 with Trait2 whereas class TestClass2 extends Trait2 with Trait1

package com.scala.test

object TestDiamond {
def main(args: Array[String]) {
val t1 = new TestClass1
t1.dummyMethod();//Invokes dummyMethod() of Trait2 as it is the last Trait in sequence
val t2 = new TestClass2
t2.dummyMethod();//Invokes dummyMethod() of Trait1 as it is the last Trait in sequence
}
}

trait BaseTrait {
def dummyMethod()
}

trait Trait1 extends BaseTrait {
override def dummyMethod() = {
println(“One”)
}
}

trait Trait2 extends BaseTrait {
override def dummyMethod() = {
println(“Two”)
}
}

class TestClass1 extends Trait1 with Trait2{
}

class TestClass2 extends Trait2 with Trait1{
}

Output
Two
One

To trait, or not to trait?

There is no firm rule, but below are few guidelines to consider.

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. 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 feel free to define such traits even if you expect Java code to inherit from it.

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. 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.

Notes

With Java 8 allowing concrete methods in interfaces, Scala 2.12 is able to compile a trait to a single interface.Before, a trait was represented as a class that held the method implementations and an interface.

Leave a Reply

Your email address will not be published. Required fields are marked *