Java doesn’t allow multiple inheritance for the fear of Deadly Diamond of Death. And to circumvent this Java introduced interfaces where in a class can extend only one other class, but implement multiple interfaces. These interfaces don’t contain any implementations. (This is going to change with Defender Methods in Java 8). Lot of other languages support interface like constructs but with greater power. And one such construct in Scala are Traits.
also read:
Traits like Interfaces in Java
Lets have a look at Trait being used as a Interface in Java. Consider a Trait Reader which provides a abstract read method.
trait Reader{ def read(source:String):String }
This trait can be extended by Scala classes like:
class StringReader extends Reader{ import scala.io.Source def read(source:String) = Source.fromString(source).mkString }
In Scala we can place imports at any line. Lets see the above code in action:
object Main extends App{ val stringReader = new StringReader println(stringReader .read("This is a string to be printed back")) println(stringReader.isInstanceOf[Reader]) }
The output is:
This is a string to be printed back true
You must be thinking: But this is what an Interface can do, how is a trait different?
Traits with method implementations
Let me edit the Reader trait to implement read method to read the contents from the String i.e what StringReader does.
trait Reader{ def read(source:String):String = Source.fromString(source).mkString }
Here is the power of traits. They can have implementations. A real advantage is that lot of times we have common implementations of certain methods in Interfaces and we end up implementing those interfaces and copying the code at all the places- A clear violation of DRY. Java is introducing a similar concept in Java 8 called Defender Methods. The main intention of introducing Defender Methods is for enhancing the APIs in such a way that it doesnt break millions of existing implementations.
Lets mix in the above trait into a class.
class Person(var name:String, var age:Int){ def getDetails = name+ " "+ age } class Employee(name:String, age:Int, var moreDetails:String) extends Person(name,age) with Reader{ override def getDetails = { val details = read(moreDetails) name + " "+age+"\n"+"More: "+details } }
We have a Employee class which extends Person class and mixes in the Reader trait. Just like in Java we use implements keyword, in Scala we use with keyword for adding a trait. When we mix in the trait, all its contents are just added as is into the class, which means the read method in the Reader trait can be invoked from the Employee class just as if the method was actually a part of Employee class.
Lets see the above classes in Action:
object Main extends App{ val employee1 = new Employee("Alex",20, "Some more details as string") println(employee1.getDetails) }
The output:
Alex 20 More: Some more details as string
These were some of the basic concepts of Traits. There’s lot of things yet to be explored, I think I will cover the advanced concepts in a subsequent article.
The complete code for the above example:
import scala.io.Source trait Reader{ def read(source:String):String = Source.fromString(source).mkString } class Person(var name:String, var age:Int){ def getDetails = name+ " "+ age } class Employee(name:String, age:Int, var moreDetails:String) extends Person(name,age) with Reader{ override def getDetails = { val details = read(moreDetails) name + " "+age+"\n"+"More: "+details } } object Main extends App{ val employee1 = new Employee("Alex",20, "Some more details as string") println(employee1.getDetails) }