Similar to an interface, a trait in Scala is a collection of abstract and non-abstract methods. A trait can be created with all of its methods abstract. In Scala, a class can inherit from more than one traits.
Traits are compiled into Java interfaces with the classes that implements the trait.
Let’s learn more about traits under the following headings:
1. Defining a Trait
Defining a is similar to class definition except that in the case of trait, the keyword trait is used. The code below defines a trait named PrintText with a print() method. Then the class PrintLine inherits the trait and implements the print() method.
trait PrintText { def print(); } class PrintLine() { def print(): Unit = { println("Welcome to Traits") } } object TraitDemo { def main(args: Array[String]): Unit = { var line = new PrintLine(); line.print(); } }
2. Another Trait Example
In the following example we, define a trait, Equal which has two methods: isEqual() and isNotEqual(). The isNotEqual() method has an implementation while the isEqual() method has not. If a class extends this trait, then it must provide implementation for the methods that are not implemented.
The class Point extends the Equal trait and therefore provides the implementation for the isEqual() method.
trait Equal { def isEqual(a: Any): Boolean def isNotEqual(a: Any): Boolean = { return !isEqual() } } class Point (posX: Int, posY: Int) extends Equal { var x: Int = x var y: Int = y def isEqual(obj: Any): Boolean = { return obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x== y } } object TraitDemo { def main(args: Array[String]): Unit = { val p1 = new Point(1, 2) val p2 = new Point(3, 4) val p3 = new Point(1, 3) println(p1.isNotEqual(p2)) println(p1.isNotEqual(1)) println(p2.isEqual(3)) } }
Note that the two points should be equal when x values are equal. Therefore, the output of the code would be:
true true false
3. Value Classes and Universal Traits
Value classes is a mechanism in Scala to prevent allocation of runtime objects. It contains a primary constructor with only one val parameter. Moreover, it contains only methods. It does not contain variable defined, like var or val. Besides, it does not contain any other objects or nested classes. Value classes cannot be extended by another class. This is made possible by extending a value class with AnyVal.
Normally, a value class cannot extend trait. However, this can be permitted by the concept of universal traits. So for a value class to extend a trait, the trait has to be converted to a universal trait by making it extend Any.
//Universal trait definition trait Printable extends Any { def print() = { println(this) } } //Class that extends the Printable trait via AnyVal class Wrapper(val theValue:Int) extends AnyVal with Printable object TraitDemo { def main(args: Array[String]): Unit = { val w = new Wrapper(4) w.print() } }
4. When Traits Can be Used
Traits can be used in the following scenarios:
- when the behaviour is not expected to be reused, then the class can be made a concrete class
- if the behaviour needs to be reused in several unrelated classes, then it could be made a trait
- if you need to inherit behaviour from Java code, then you need to use an abstract class
- if efficiency is desired, then a class might be the best option
- if you will need to distribute the code in compiled form and outside groups need to write class inheriting from it, then you and abstract class could be the way to go