Scala Traits

Traits in Scala are similar to interfaces in Java. However, they offer substantial enhancements to Java interfaces. They can also be used in Java classes under certain conditions.

Traits encapsulate methods and fields which can be reused and mixed with classes. Scala allows both abstract and concrete methods and variables, unlike Java interfaces which have only abstract methods. So in some sense traits are like similar to classes except that it has a keyword trait in front of it when it is defined.

Let’s define a trait which has two abstract variables and one abstract method. See Below

trait IceCream{
  val name : String
  val price : Int
  def getDiscount():Double
}

Extending Traits

Traits can be extended to create new traits. See Below

trait IceCream{
  val name : String
  val price : Int
  def getDiscount():Double
}

trait FlavoredIceCream extends IceCream{
  val flavour : String
}

While extending traits it is not necessary to implement the abstract methods. It is also possible to create traits with a mix of abstract and concrete methods which was not possible in Java. See Below

trait IceCream{
  val name : String
  val price : Int
  def getDiscount:Double
  def getSellingPrice:Double = (1 - getDiscount) * price
}

trait FlavoredIceCream extends IceCream{
  val flavour : String
}

Extending Classes with Traits

Once a trait is defined it can be used to extend a class to exhibit a certain behaviour by implementing all the abstract methods and variables defined in the trait. Let’s see the example. See Below.

trait IceCream{
  val name : String
  val price : Int
  def getDiscount():Double
  def getSellingPrice:Double
}

trait FlavoredIceCream extends IceCream{
  val flavour : String
}

class chocolateIceCream(pName:String,pPrice:Int) extends FlavoredIceCream{
  override val name: String = pName
  override val price: Int = pPrice
  override val flavour: String = "Chocolate"

  override def getDiscount(): Double = {
    if (price >= 10) 0.1 else 0.2
  }
  override def getSellingPrice(): Double = (1-getDiscount)*price
}

val belgianDark = new chocolateIceCream("belgian dark", pPrice = 10)

//Prints - beligian dark
println(belgianDark.name)

//Prints - 10
println(belgianDark.price)

//Prints - 9
println(belgianDark.getSellingPrice)

In the above example, class chocolateIceCream implements FlavoredIceCream trait. All the abstract members and variables are implemented in the trait. override keyword is used before the abstract methods before they are implemented in the class. Traits can also be used to extend abstract classes.

Extending classes with multiple traits

It is possible to implement not just single traits but multiple traits similar to Java using the with operator. The rules remain the same. Say suppose we want to add manufacturer information about the ice cream. See Below

trait Manufacturer{
  val factoryName : String
  val location : String
  def getDescription: String
}

trait IceCream{
  val name : String
  val price : Int
  def getDiscount:Double
  def getSellingPrice:Double = (1 - getDiscount) * price
}

trait FlavoredIceCream extends IceCream{
  val flavour : String
}

class chocolateIceCream(pName:String, pPrice:Int, pManufName:String, pLocation:String) extends FlavoredIceCream with Manufacturer{
  override val name: String = pName
  override val price: Int = pPrice
  override val flavour: String = "Chocolate"
  override val factoryName: String = pManufName
  override val location: String = pLocation

  override def getDiscount: Double = if (price >= 10) 0.1 else 0.2
  override def getDescription: String = factoryName + ' - ' + location
}

val belgianDark = new chocolateIceCream("belgian dark",10,"Ben & Jerry","US")

//Prints - beligian dark
println(belgianDark.name)
//Prints - 10
println(belgianDark.price)
//Prints - 9
println(belgianDark.getSellingPrice)

//Prints - Ben & Jerry - US
println(belgianDark.getDescription)

Let’s make traits a bit more interesting. Scala allows for modifying the class composition without modifying the class hierarchy.

See the example below. There are two objects created – bread and vanillaIceCream. The bread object does not extend the IceCream trait however if you observe the vanillaIceCream object extends the trait IceCream and hence has additional members flavor and color available in the object.

trait IceCream{
  val flavor : String
  val color: String
}

class Product(pName: String, pPrice: Int, pDiscount: Double) {
  val name = pName
  val price = pPrice
  val discount = pDiscount

  def getSellingPrice:Double = (1-discount)*price
}

val bread = new Product("Seven Seeds",10,0.2)
val vanillaIceCream = new Product("IceCream Cup", 20, 0.1) with IceCream{
  override val flavor: String = "Vanilla"
  override val color: String = "White"
}

println(bread.name)
println(vanillaIceCream.name)
println(vanillaIceCream.flavor)

Limiting the classes which extend Traits

It is possible to limit which classes are allowed to extend a particular trait. There are a couple of ways to do that one is by using an abstract class before the trait. The other method is providing the class information in the trait itself. See Below

trait IceCream{
  this: Product =>
  val name : String
  val price : Int
  def getDiscount():Double
}

In the above code the only the class Product can implement the trait IceCream.

Sealed Traits

A sealed trait can be extended only in the same file in which it is declared. They are in a way used to limit the number of subtypes. Usually, they are used as an alternate to enumerations in Scala as. Sealing a trait is done by placing a modifier sealed before the trait keyword. See Below

sealed trait IceCream{
  val name : String
  val price : Int
  def getDiscount():Double
}

Hope this blog entry has given you a good idea. Next we look at case objects and enumerations.

Till then!

Leave a Comment