Scala Nuggets: Understanding Traits

Table of contents
Reading Time: 4 minutes

Inphina provides specialized Scala consulting, training and offshoring … Learn more about Scala@Inphina!

If you are coming from a Java background, then traits are interfaces++.  In scala, trait is a complete mixin solution where we can provide implementation in the trait as well as have classes mix-in the trait either when we declare classes or even when we are creating an instance of the class.

Hence, both of these are valid if Metering was a trait, which can do any kind of counting

class Tablet extends Device with Metering { …

val anotherPhone = new Phone with Metering { …

This is powerful where we can give a trait to a class at the time of instantiation.

Let us consider our business scenario where we want any new device to be metered. Let us say that there are two devices for now a Tablet and a Phone which have the following definitions

[sourcecode language=”scala”]
abstract class Device {
def setPrice(x: Double)
}

class Phone extends Device {
def setPrice(x: Double) = {
println("the cost for the phone is " + x)
}
}

class Tablet extends Device with Metering {
def setPrice(x: Double): Unit = {
println("Price for the tablet is " + x)
addObserver(DeviceCounter)
notifyObservers
}
}

[/sourcecode]

As you would notice, the Phone class does not mixin the trait Metering, whereas the Tablet class does. Let us look at the Metering trait

[sourcecode language=”scala”]
trait Metering {
// any type with a method signature given below can be used for Metering
type anyObserver = { def receiveUpdate(subject: Any) }
private var observers = List[anyObserver]()
def addObserver(observer: anyObserver) = observers ::= observer
// we define a function literal with foreach
def notifyObservers = observers foreach (_.receiveUpdate(this))
}
[/sourcecode]

Here, the Metering trait allows observers to be added to it so that it can call a notify on the observers. We have defined implementations too in the trait unlike Java. The first line defines a type for an Observer. This is a structural type of the form { def receiveUpdate(subject:Any) }. Structural types specify only the structure a subtype must support. In our case, the structural type is defined by a having a method with a particular signature.
In our case let us add the observer be added as the following. It just increments the counter.

[sourcecode language=”scala”]
object DeviceCounter {
var count = 0
def receiveUpdate(subject: Any) = {
count += 1
println("Counter notified and the count is " + count)
}
}
[/sourcecode]

Now, let us look at the following code block

[sourcecode language=”scala”]
object ProvisioningService {
def main(args: Array[String]) {

val tablet = new Tablet
tablet.setPrice(10.0)

val phone = new Phone
phone.setPrice(18.0)

val anotherPhone = new Phone with Metering {
addObserver(DeviceCounter)
notifyObservers
}
anotherPhone.setPrice(89.0)
}
}
[/sourcecode]

We create an instance of Tablet and call its setPrice method. Since Tablet is mixed in with the Metering Trait, the counter would get incremented. Next, we instantiate a Phone and set its price. At this point the counter is not incremented since Phone does not mixin the Metering trait. But, look what we do next, we mixin the trait at the time of creating the next phone instance. Hence, the anotherPhone instance has the trait and hence also participates in the metering. This is something that we cannot do in Java. The result is the following output of code.

Price for the tablet is 10.0
Counter notified and the count is 1
the cost for the phone is 18.0
Counter notified and the count is 2
the cost for the phone is 89.0

Now, let us see how traits are constructed and what is their sequence of construction. Traits do not support auxiliary constructors. We cannot pass arguments to the primary constructor either to pass on values to the defined attributes. We can however, initialize fields with default values or leave them abstract so that the class with the trait can take care of it.

Let us look at the following code,

[sourcecode language=”scala”]
object Traitors {

def main(args: Array[String]) {
println("Creating the concrete class")
new ConcreteClass
println("After Creating the concrete class")
}

trait TraitOne {
val x = "TraitOne"
println(" in TraitOne: x = " + x)
}
trait TraitTwo {
val y = "TraitTwo"
println(" in TraitTwo: y = " + y)
}
abstract class BaseClass {
val b = "BaseClass"
def hello
println(" in BaseClass = " + b)
}
class ConcreteClass extends BaseClass with TraitOne with TraitTwo {
val c = "ConcreteClass"
def hello = print("hello")
println(" in ConcreteClass = " + c)
}

}
[/sourcecode]

When we execute the main method, we get the following output

Creating the concrete class
in BaseClass = BaseClass
in TraitOne: x = TraitOne
in TraitTwo: y = TraitTwo
in ConcreteClass = ConcreteClass
After Creating the concrete class

So, first the base class is constructed, then the trait one and trait two and then our concrete class. If we change the order of mixin of traits “with TraitTwo with TraitOne” instead of ” with TraitOne with TraitTwo” then the order of construction would change to TraitTwo being constructed before TraitOne.

So far so good and I really like the part where we can mixin a trait at the time of creating an instance. That is huge! Now onto something that I dont like. If I have a class in Java which implements two interfaces I would write it like

[sourcecode language=”java”]
public class A implements InterfaceOne, InterfaceTwo{
}
[/sourcecode]

Now, if I need to do the same thing in scala, i would have to write
[sourcecode language=”scala”]
class AnotherClass extends TraitOne with TraitTwo{
[/sourcecode]

As you would notice, I have to use the extend keyword even if I am not extending any other class. If I am mixin only interfaces then the first interface is extended and then I can use with. Well, if you look at it other way it makes sense because Trait is not an interface and a class can extend Traits and a Trait can extend a class. So all in all it is good, just that like a few other things, it takes getting used to 🙂

As always, code is available on github.

Written by 

Vikas is the CEO and Co-Founder of Knoldus Inc. Knoldus does niche Reactive and Big Data product development on Scala, Spark, and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). Vikas has been working in the cutting edge tech industry for 20+ years. He was an ardent fan of Java with multiple high load enterprise systems to boast of till he met Scala. His current passions include utilizing the power of Scala, Akka and Play to make Reactive and Big Data systems for niche startups and enterprises who would like to change the way software is developed. To know more, send a mail to hello@knoldus.com or visit www.knoldus.com

1 thought on “Scala Nuggets: Understanding Traits5 min read

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading