🇧🇷 Leia em Português

Classes, Objects and Traits in Scala

A black and white image of a laptop and a notebook faded and on the middle of the image there is the Scala logo in red for the post 4

This post is also known as “My saga learning Scala - Part 4“

New suggestions!

Apparently, at least one person is reading these Scala posts! Bram Elfrink send me a link to a series of Scala classes given at Twitter and it seems pretty cool! Make sure you check it out! Thanks, Bram!

Objects

In the last post, I presented how to work with class attributes and methods, especially how to limit the usage of both the class, the attributes, and the methods. But classes are only one type of object in Scala. As we discussed in Part 2, when you want to run something in Scala, we create an object and not a class. So what’s the difference between one and the other?

When we create a new class, we can instantiate it, such as:

class Person(val name: String) 
val mary = new Person("Mary") 

If I create a new person, this will create a new instance:

val john = new Person("John")

The instance john is not the same as the mary instance, because each time we call the new Person we create a new instance.

In Scala, an object will always have a single instance, no matter how many times you “instantiate” it. That’s why an object is called a singleton.

I don’t even need the new keyword to create an instance, because we actually don’t need to instantiate the object at all. When you create an object, it is already instantiated, so you can simply use it.

Because of this characteristics, a Scala object can’t have a constructor, this is, it can’t receive any parameters.

In the following example, you will see that both values are actually the same instance.

object Person
val mary = Person // same as `new Person`
val john = Person // same as `new Person`

println(mary == john) // true

Because an object is always an instance - and only one instance - you can always access attributes and methods inside an object directly:

object House {
  val has_kitchen: Boolean = true  
}

println(House.has_kitchen) // true

Another interesting thing is that you can also have a class with the same name as an object, in the same file! The following example works just fine:

object Person {
  num_eyes = 2
}

class Person(name: String)

val person = Person // object
val mary = new Person("Mary") // class

Overloading

In the last post that we can create a class with multiple constructors. We could do that by simply writing two constructor methods with the same name. This feature is called overloading and, as you can see, can be pretty useful:

class Person(name: String) { 
    def greet(name: String) = println(s"${this.name} says: Hello, $name")
    // overloading
    def greet = println(s"Hi! My name is $name")

val john = new Person("John")

println(john.greet) // Hi! My name is John!
println(john.greet("Mary")) // John says: Hello, Mary

Abstraction!

Now that we’ve seen how classes and objects work, it is time to understand how to use abstract objects that can be inherited by a class. Inheritance strongly supports the concept of reusability, allowing classes to reuse properties and methods already available in other classes, thus, avoiding code duplication!

Abstract class

An abstract class is pretty much a normal class with the abstract reserved word in front of it. It can have a constructor and receive parameters (just like a normal class would) and can have methods and attributes that can be overwritten.

abstract class Animal {
  val creatureType: String
  def eat: Unit
}

class Dog extends Animal {
  override val creatureType: String = "Dog"
  override def eat: Unit = ???
}

val animal = new Animal  // Error! class Animal is abstract; cannot be instantiated
val dog = new Dog // Works!

You can also require parameters on an abstract class by adding a constructor such as:

abstract class Animal(val identification: String)
class Dog(val name: String) extends Animal(name)

However, there is a catch! You can only extend 1 abstract class on a regular, simple class. So our class Dog won’t extend any other class other than Animal. The model of only allowing a class to extend from just one other single class is called single inheritance. I tried to understand why would a language allow only a single inheritance system and the explanation I found for C# was:

C# does not support multiple inheritance, because they reasoned that adding multiple inheritance added too much complexity to C# while providing too little benefit.

Traits

If you take a look at what’s written about traits in the Programming in Scala First Edition, you’ll see that:

Traits are a fundamental unit of code reuse in Scala. [1]

Similar to abstract classes, traits are a way to gather functions and methods in a single place and re-use it in other classes. You can define a trait as:

trait Carnivore {
  def chasePrey: Unit
}

But, there are two main differences (that I could see) between traits and abstract classes:

  • Traits don’t have a constructor, so they can’t receive parameters
  • A single class can inherit multiple traits

So we can have such as:

trait Animal {
  val isAlive: Boolean = true
}

trait Carnivore {
  val creatureType: String = "Carnivore"
  def eat(creature: String): String = s"${this.creatureType} eats $creature"
}

trait ColdBlooded {
  val environment: String = "Warm"
}

class Crocodile extends Animal with Carnivore with ColdBlooded {
  override val creatureType: String = "Crocodile"
}
class Dog extends Animal with Carnivore {
  override val creatureType: String = "Dog"
}

val dog = new Dog
val croc = new Crocodile

println(croc.eat(dog.creatureType)) // Crocodile eats Dog

Choosing between an abstract class and a trait

Ok, amazing! But now what? Which one do I use?

There is a really long explanation on which one to use, but the general idea is: when in doubt, use traits! From the explanation I just mentioned:

If you still do not know, after considering the above, then start by making it as a trait. You can always change it later, and in general using a trait keeps more options open.


That was it for today! This took me a while and I am not as active as I was on my Scala course. There was a chapter on Generics that killed my motivation, but I haven’t given up! Hopefully, I’ll write about it eventually, but who knows!? I never imagined getting to Part 4! 🙂


Cheers!
Letícia

Comments