Scala classes/objects/traits a little more in depth

Recap on what a class, object and trait are…

A class is analogous to a C# class which is created on a per instance bases. An object is a singleton, analogous to a static class in C# and a trait can how declarations of functions (without implementations) or include implementations which is analogous to a C# abstract class.

So in summary

ScalaC#
classclass
objectstatic class
traitabstract class

Class

As previously mentioned, a class is a type which we can create an instance of, and cannot have abstract (or non-implemented) functions (see trait for abstract/interface types)

class Rectangle(width: Int, height: Int) {
  def area = width * height
}

and we can extend/derive from this using the extend keyword, giving us

class Square(side: Int) extends Rectangle(side, side) {
}

Object

As previously stated, an object can be viewed as a static class (in C#), giving us

object ShapeFactory {
  def createSquare(side: Int) = 
     new Square(side)
  def createRectangle(width: Int, height: Int) = 
     new Rectangle(width, height)
}

In use, we do not need to use the new keyword (just like C#), so we can use this factory like this

val square = ShapeFactory.createSquare(5)
assert(square.area == 25)

Trait

A trait can be viewed as being analogous to an abstract class. We can have functions with implementations or no implementation so that any class which extends this trait must implement the unimplemented functions. Here’s a trait with a single abstract function (no implementation) named area and an implementation of a function named name

trait Shape {
  def area : Int
  
  def name = "Shape"
}

We cannot create a trait without implementing the area function, but this doesn’t mean we must create a class (as such), we can create what C# might call and anonymous type, for example

val s = new Shape {
   override def area: Int = 20
}

But ofcourse, we can also extend the trait with another trait or a concrete implementation as a class, for example

class Rectangle(width: Int, height: Int) extends Shape {
  def area = width * height
}

Functions, by default, are virtual (as per Java) and so we can override the name function, for example

class Rectangle(width: Int, height: Int) extends Shape{
  def area = width * height

  override def name: String = "Rectangle"
}

If we want to stop a function being overridden we can use the final keyword (again like java). So Shape might be defined as

trait Shape {
  def area : Int

  final def name = "Shape"
}

Trait as an interface

So one thing you might notice is that in C# and Java we have the concept of an interface. Scala doesn’t support a specific interface keyword. So in this way it’s similar to C++ in that we can simply use a trait without any implementations.

For example

trait Shape {
   def area : Int
}

We can extend traits, for example

trait Triangle extends Shape {
  def isRightAngle : Boolean
}

and ofcourse we could have implemented some functions within the extended type. To implement a class from a Triangle we can just write the following

class RightAngleTriangle(adjacent: Int, opposite: Int) extends Triangle {
  override def area: Int = (adjacent * opposite) / 2
  override def isRightAngle: Boolean = true
}