Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Inheritance / Overriding / Traits

0.00/5 (No votes)
20 Oct 2015 1  
In this post, we will talk about how to create abstract classes, inheritance and also traits.

In this post, we will talk about how to create abstract classes, inheritance and also traits.

<Rant>

One thing I wanted to mention is that in Scala the focus is clearly on creating immutable data structures. Which is what YOU should be trying to achieve WHEREVER possible.

This means that you should be using var sparingly, really think about whether you DO need that field to be mutable, I would say ALWAYS favor val if you can, and only use var as a last resort.

In my last post, I did actually show some code that true Scala devs would probably not do, which is to accept var as constructor parameters. This was to illustrate a point about properties, but in reality this should have been using val instead.

</Rant>

Anyway rant over. Let’s carry on with the guts of this post, which to start of will be abstract classes.

Abstract Classes

In Scala, much the same as other OO languages (looking at you here .NET), you can declare a class as abstract using the abstract keyword.

Unlike .NET, you do NOT mark the individual methods as “abstract”, you only make the class abstract. A method is abstract if the class is abstract and there is no implementation.

Here is an example of an abstract class, with a simple abstract method:

//Primary constructor
abstract class PersonBase(val firstName: String, val lastName: String) {
 
  def ReportsTo() : String
 
  override def toString: String = {
    s"firstname: $firstName, lastname: $lastName, reportsTo : $ReportsTo"
  }
}

Inheritance

So now that we have an abstract class, how do we extend this abstract class. Let's see a couple of examples that extend the abstract class we specified above.

Here we have a Supervisor and CEO class, both of which want to use the PersonBase class as a super type.

So how do we do that in Scala?

Well it is actually quite simple, let’s see an example of the Supervisor and CEO classes.

class Supervisor(override val firstName: String, override val lastName: String, val budget: BigDecimal)
  extends PersonBase(firstName, lastName)
{
  override def ReportsTo(): String = "MD"
} 
 
class CEO(override val firstName: String, override val lastName: String, val budget: BigDecimal)
  extends PersonBase(firstName, lastName)
{
  override def ReportsTo(): String = "None"
}

There are a couple of things to note above:

  • We use the extends keyword to extend the PersonBase class (which makes PersonBase the super type of the current type (i.e., Supervisor / CEO)
  • We use the override keyword a couple of times in the primary constructor for these new Supervisor / CEO classes
  • We use the override keyword on the method def to override the super types ReportsTo() method

In a nut shell, that is the basics of how to inherit from a class and override methods / primary constructor parameters.

Interfaces

In Scala, there are no interfaces, instead there are traits. We will look at those next.

Traits

Traits are like abstract classes, and are used to define object types by specifying the signature of the supported methods. Traits may be partially implemented, and a type may also inherit from MULTIPLE traits. What multiple inheritance, yep you can do that.

Yikes!

In contrast to classes, traits may not have constructor parameters.

Let's see an example of a simple trait that mimics a 2 input AND logic gate, which has a NAND method too.

In this simple example, we have an abstract method “and” which needs to implemented, but we also have a nand method which is already implemented. So the inheritor of this trait ONLY needs to supply an implementation for the “and” method.

trait AndGate {
  def and(x1: Boolean, x2: Boolean): Boolean
  def nand(x1: Boolean, x2: Boolean): Boolean = !and(x1, x2)
} 
 
class LogicGate() extends AndGate {
 
  def and(x1: Boolean, x2: Boolean): Boolean =
    x1 && x2 
}

Which we are able to use like this:

object ClassesDemo {
  def main(args: Array[String]) =
  {
 
    val lg = new LogicGate()
    val and1Result = lg.and(true,false)
    val nand1Result = lg.nand(true,false)
    System.out.print(s"lg.and : $and1Result,lg.nand : $nand1Result \r\n")
 
    val and2Result = lg.and(true,true)
    val nand2Result = lg.nand(true,true)
    System.out.print(s"lg.and : $and2Result,lg.nand : $nand2Result \r\n") 
 
    System.in.read()
 
    ()
  } 
}

Which when run looks like this:

image

The other interesting thing about traits is that you may have multiple traits, and they may even have the same method. For example, what about this, what do you think happens here:

trait LoggerBase
{
  def log(input : String): Unit
} 
 
trait Logger extends LoggerBase {
  override def log(input : String): Unit = {
    System.out.print(s"Logger :input = '$input'\r\n")
  }
}
 
trait AnotherLogger extends LoggerBase {
  override def log(input : String): Unit = {
    System.out.print(s"AnotherLogger input = '$input'\r\n")
  }
} 
 
//This class inherits from 2 Traits, both with same
//method implemented
class SomeWeirdLogger() extends Logger with AnotherLogger {
}

Do you think we will see this output:

AnotherLogger input = ‘What gives’

or this output:

Logger input = ‘What gives’

Well, we actually get this output:

image

But why is this?

What would happen if we reversed the order to this:

trait LoggerBase
{
  def log(input : String): Unit
} 
 
trait Logger extends LoggerBase {
  override def log(input : String): Unit = {
    System.out.print(s"Logger :input = '$input'\r\n")
  }
}
 
trait AnotherLogger extends LoggerBase {
  override def log(input : String): Unit = {
    System.out.print(s"AnotherLogger input = '$input'\r\n")
  }
} 
 
//This class inherits from 2 Traits, both with same
//method implemented
class SomeWeirdLogger() extends AnotherLogger with Logger  {
}

Where we have swapped the order of the inherited traits.

Now we get this output. Mmm Strange.

image

Basically traits DO HAVE an order, and the outer most one wins.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here