Scala Tutorials Part #8 - Traits
Originally Posted On : 23 Jan 2017
This is part 8 of the scala tutorial series. Check here for the full series.
- Introduction to traits
- A basic trait - Syntax explanation
- Concept of abstract variables in java
- Type annotations for abstract variables
- Mixing abstract and concrete members
- Java syntax differences
- Extending traits
- The with keyword
- Mixin class composition
Traits are a new concept to scala just like case classes are. They complement to the already existing features in OOP.
A better way to understand traits is comparing it with java’s language features.
They are kind of similar to interfaces, but they can have implementations of methods, which works like abstract classes except that traits do not have constructors.
Best way to understand them is via examples. So let’s get our hands dirty.
Declaring a trait begins with the
trait keyword and then the trait name, followed by the body/content of the trait.
If you are coming from a java background, then the above example might confuse you. In java, there is no such thing as abstract variable in java.
Only methods and classes can be abstract in java, variables cannot.
And since we do not have abstract variables, we also cannot override variables.
The reason why scala is different in this aspect i.e why it has abstract variables has to do with two functional programming concepts.
One is uniform access principle
which we saw earlier in classes and another is referential transparency.
As these concepts are complex they require additional posts of their own, we will explore them later.
Since scala strives to see methods and values as the same using uniform access principle i.e everything is a value type we have abstract variables as well.
Abstract variables should have their types annotated regardless of whether they are in traits or in abstract classes. I should have explained this concept when I was explaining scala classes but since traits also use them I thought I will explain it here.
If we do not mention the type, then we get an error as below.
Remember that scala has local type inference, so we need to annotate the types explicitly.
For methods the scenario is slightly different. We can declare a method such as below.
It is a valid syntax and compiles without error.
There are subtle differences to note here.
def noExplicitTypeAnnotation is the same as
def noExplicitTypeAnnotation() : Unit. The type
Unit is equivalent of
void in java. It indicates the absence of an element in the literal void sense. This is not to be confused with other types that exist in scala such as
Null which I will cover later.
Unit type makes no sense when applied to variables and hence the compiler does infer to that type as it does for methods.
We can however assign
Unit as a type to a variable.
It would print
() but it doesn’t make sense to annotate a variable with a
So far we have seen only abstract variables. In reality we would use a mixture of abstract variables,methods and/or concrete variables/methods.
Let’s take an example.
As we saw in the methods tutorial, if we omit the part after the
= then it becomes an abstract method.
Notice that type inference works for concrete variables since it is known at compile time and type annotation is optional.
In java, we have interfaces and abstract classes and we extend abstract classes/classes and implement interfaces.
Scala does not have both interface and the implements keyword.
There are subtle differences in syntax and there is also a new keyword called
with which we will explore below.
Similar to java, scala has the
extends keyword which can be used for extending classes and traits.
Traits can be extended by other traits,abstract classes,concrete classes and case classes as well.
Traits extending traits
Since traits cannot be instantiated, it is not necessary that the abstract members have to implemented.
But if we need to implement any of the concrete members, we need the override modifier.
A correct version of the above would be.
Abstract classes extending traits
Abstract classes can also extend traits.
The same principles which apply to traits extending traits are also applicable here.
Classes extending traits
Since classes are concrete i.e instances can be created, the abstract members of the trait should be implemented.
A correctly implemented version of the class would be as follows.
We did not implement the
getTaxOnPrice method and the variable
category variable which is fine since they are concrete members. The type annotations are of course
optional, they were present in my code example since Intellij auto-generated them.
If we need to change the logic we can of course override their implementation.
Since case class inheritance is a complex topic, I will be explaining that in a dedicated tutorial.
Since there is no concept of interfaces and implements keyword in scala, how would you go about extending a trait and then a class at the same time?
In java you would typically do it like
Scala has a new keyword for it.
Let’s consider another abstract class called
Now since a book is a product we can combine the logic.
Now imagine that there are situations where we need not extend
Product class and situations where we also need to extend.
This is impossible to solve with the OOP concepts that exist in java since we need to declare what the
ScienceBook class extends beforehand.
Scala has something called mixins which can enable to do a mix of class compositions where we can choose to extend the
Product features without modifying
its original class hierarchy.
To demonstrate, we need to make a simple change to our abstract class
Product and change it to a trait.
Next during instance declaration we can now extend the product trait with a different syntax as below.
Since we are creating an actual instance we need to override the abstract variables.
ScienceBook class however remains intact in its logic i.e it need not extend the
Product trait. Now we have a the functionality of the Book,ScienceBook,Product
classes and traits in a pretty neatly laid out way.
The scala doc article on understanding these mixins is also good.
Traits are more related to abstract classes than to interfaces. Main difference being traits do not have a constructor. Whenever you need to have a constructor for your OOP logic, then an abstract class will suit better, for all else traits are much better.
There are much more complex topics that traits open up, two in particular that I will cover in later tutorials are
1) Trait linearization - Since traits allow definitions and we can extend multiple traits how is the old problem of multiple inheritance handled?
2) Sealed traits - The sealed keyword says that classes in other files cannot extend the trait, but there is much more to this.
This brings an end to this article. I have covered the introductory and easier topics in this post and plan to cover advanced topics one at a time in later articles.