Scala Tutorial - Learn How To Extend Multiple Traits
Overview
In this tutorial, we will learn how to create traits with type parameters and show how to have a class extend multiple traits by making use of the with keyword.
We will update the examples from the previous tutorials on Learn How To Create And Extend Trait and Learn How to Create Trait With Type Parameters.
Steps
1. Create a trait which will define the methods for a data access layer
Let's start with defining a simple trait called DonutShoppingCartDao and it will provide the method signatures to represent a Data Access Layer.
println("Step 1: Create a trait with type which will define the methods for a data access layer")
trait DonutShoppingCartDao[A] {
def add(donut: A): Long
def update(donut: A): Boolean
def search(donut: A): A
def delete(donut: A): Boolean
}
NOTE:
- The trait is defined with type parameter DonutShoppingCartDao[A]. If you are unsure about type parameters, please review the previous tutorial.
2. Create a second trait which will define the methods for checking donut inventory
Next, we create another trait called DonutInventoryService[A] which encapsulates methods to query donut inventories such as checkStockQuantity() method.
println("\nStep 2: Create a second trait which will define the methods for checking donut inventory")
trait DonutInventoryService[A] {
def checkStockQuantity(donut: A): Int
}
NOTE:
- Trait DonutInventoryService[A] is also defined with type parameter.
3. Create a DonutShoppingCart class which extends multiple traits namely trait DonutShoppingCartDao and trait DonutInventoryService
We then define a class named DonutShoppingCart[A] which extends both the traits DonutShoppingCartDao[A] and DonutInventoryService[A] as shown below.
println("\nStep 3: Create a DonutShoppingCart class which extends multiple traits namely trait DonutShoppingCartDao and trait DonutInventoryService")
class DonutShoppingCart[A] extends DonutShoppingCartDao[A] with DonutInventoryService[A] {
override def add(donut: A): Long = {
println(s"DonutShoppingCart-> add method -> donut: $donut")
1
}
override def update(donut: A): Boolean = {
println(s"DonutShoppingCart-> update method -> donut: $donut")
true
}
override def search(donut: A): A = {
println(s"DonutShoppingCart-> search method -> donut: $donut")
donut
}
override def delete(donut: A): Boolean = {
println(s"DonutShoppingCart-> delete method -> donut: $donut")
true
}
override def checkStockQuantity(donut: A): Int = {
println(s"DonutShoppingCart-> checkStockQuantity method -> donut: $donut")
10
}
}
NOTE:
- We've made use of the with keyword to extend multiple traits.
4. Create an instance of DonutShoppingCart and call the add, update, search and delete methods
You can now create an instance of DonutShoppingCart and call the add, update, search and delete methods which were inherited from trait DonutShoppingCartDao[A]
println("\nStep 4: Create an instance of DonutShoppingCart and call the add, update, search and delete methods")
val donutShoppingCart: DonutShoppingCart[String] = new DonutShoppingCart[String]()
donutShoppingCart.add("Vanilla Donut")
donutShoppingCart.update("Vanilla Donut")
donutShoppingCart.search("Vanilla Donut")
donutShoppingCart.delete("Vanilla Donut")
NOTE:
- To keep the example simple, we are passing a type of String although in a real world application you would most likely have custom type classes.
You should see the following output when you run your Scala application in IntelliJ:
Step 4: Create an instance of DonutShoppingCart and call the add, update, search and delete methods
DonutShoppingCart-> add method -> donut: Vanilla Donut
DonutShoppingCart-> update method -> donut: Vanilla Donut
DonutShoppingCart-> search method -> donut: Vanilla Donut
DonutShoppingCart-> delete method -> donut: Vanilla Donut
5. Call the checkStockQuantity method which was inherited from trait DonutInventoryService
Since class DonutShoppingCart also extended the second trait DonutInventoryService[A], you also have access to the checkStockQuantity() method as shown below.
println("\nStep 5: Call the checkStockQuantity method which was inherited from trait DonutInventoryService")
donutShoppingCart.checkStockQuantity("Vanilla Donut")
You should see the following output when you run your Scala application in IntelliJ:
Step 5: Call the checkStockQuantity method which was inherited from trait DonutInventoryService
DonutShoppingCart-> checkStockQuantity method -> donut: Vanilla Donut
Summary
In this tutorial, we went over the following:
- Create a trait which will define the methods for a data access layer
- Create a second trait which will define the methods for checking donut inventory
- Create a DonutShoppingCart class which extends multiple traits namely trait DonutShoppingCartDao and trait DonutInventoryService
- Create an instance of DonutShoppingCart and call the add, update, search and delete methods
- Call the checkStockQuantity method which was inherited from trait DonutInventoryService
Tip
- We've kept the trait type parameters example simple but it would be good to review variance namely covariance and contra-variance type parameters.
- In upcoming tutorials in this Chapter, we will also show how to use traits to build some pure Functional Programming constructs such as Monoids and Functors and much more!
Source Code
The source code is available on the allaboutscala GitHub repository.
What's Next
In the next tutorial, I will show you how use traits for Dependency Injection.