Traits: Adding Concrete Behaviors (Scala 3 Video)
In these examples we start adding concrete behaviors to traits, and then use those traits as mixins.
Example
If you want to model animals that have tails — and dozens of other features — you might start by modeling the tail with a trait. This time we’ll make the trait methods concrete rather than abstract:
trait HasTail:
def startTail = "Tail started"
def stopTail = "Tail stopped"
Those are concrete methods because they have an implementation. Technically they are defined as two methods that yield String
results, so you can show the method return type as well:
trait HasTail:
def startTail: String = "Tail started"
def stopTail: String = "Tail stopped"
Similarly, methods related to legs might also have a default implementation:
trait HasLegs:
def walk = "Walking"
def run = "Running"
def standStill = "Standing still"
Then when you get to speaking, you might want to leave those methods abstract, because different implementors will implement a speak
method in different ways:
trait CanSpeak:
def speak: String
Jumping ahead a little bit, traits in Scala 3 can also have parameters, just like classes:
trait Pet(name: String)
Given all these traits, you can now create classes by extending those traits, and implementing the methods as desired:
class Dog(name: String) extends Pet(name), HasTail, HasLegs, CanSpeak:
def speak = "Woof"
override def stopTail: String = "Tail stopped (reluctantly)"
class Cat(name: String) extends Pet(name), HasTail, HasLegs, CanSpeak:
def speak = "Meow"
val d = Dog("Fido")
d.walk
d.run
d.standStill
d.speak
val c = Cat("Morris")
Complete example
Here’s a complete example from the video:
trait HasTail:
def startTail: String = "Tail started"
def stopTail: String = "Tail stopped"
trait HasLegs:
def walk = "Walking"
def run = "Running"
def standStill = "Standing still"
trait CanSpeak:
def speak: String
trait Pet(name: String):
def myName: String = s"My name is $name."
class Dog(name: String) extends Pet(name), HasTail, HasLegs, CanSpeak:
def speak = "Woof"
override def stopTail: String =
"Tail stopped (reluctantly)"
@main def main =
val d = Dog("Fido")
println(d.walk)
println(d.run)
println(d.standStill)
println(d.speak)
println(d.myName)
Real-world example
For this example I begin with my best-selling book, Functional Programming, Simplified.
It’s very common to have traits that declare abstract methods. To make a method abstract, just declare its name and its value (i.e., its return type). Here are some examples of traits with abstract methods that I later combine into a complete class
:
import java.time.LocalDate
trait Product: // 1st
def cost: Double
def price: Double
def sku: String
def title: String
def description: String
trait BookTrait extends Product: // 3rd (tie)
def title: String
def mainAuthor: String
def otherAuthors: Seq[String]
def publicationDate: LocalDate
def language: String
trait PaperbackBookTrait extends BookTrait: // 2nd
def pageCount: Int
def isbn: String
trait EBookTrait extends BookTrait: // 3rd (tie)
def fileSizeInBytes: Int
def hasEnhancedTypesetting: Boolean
def hasFreePreview: Boolean
class PaperbackBook( // 4th
val title: String,
val mainAuthor: String,
val otherAuthors: Seq[String],
val isbn: String,
val pageCount: Int,
val publicationDate: LocalDate,
val language: String
) extends PaperbackBookTrait:
def cost = ???
def price = ???
def sku = ???
end PaperbackBook
val scalaCookbook = PaperbackBook( // 5th
title = "Scala Cookbook",
mainAuthor = "Alvin Alexander",
otherAuthors = Seq[String](),
isbn = "1492051543",
pageCount = 820,
publicationDate = LocalDate.parse("2020-12-10"),
language = "English"
)
Note:A key in the Scala design process and domain modeling is that the traits are granular and single-purpose, and we later combine them to create a complete class.
Update: All of my new videos are now on
LearnScala.dev