0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Inheritance and Polymorphism

Explore object-oriented principles like inheritance, abstract classes, and polymorphism in Scala.

Inheritance and Polymorphism is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 2 of 2. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 2 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What is Inheritance?

Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows a new class to inherit properties and behaviors (methods) from an existing class.

Think of it as a 'parent-child' relationship. The 'child' class (subclass) reuses code from the 'parent' class (superclass), saving time and ensuring consistency.

  • Promotes code reuse.
  • Establishes an 'is-a' relationship (e.g., a Dog is an Animal).
  • Helps organize code into a hierarchy.

Your First Base Class

Let's define a simple Animal class. This will be our base class, also known as a superclass or parent class. It has a basic behavior: to speak.

Run the code to see our generic animal in action!

class Animal {
  val name: String = "Generic Animal"
  def speak(): Unit = {
    println(s"$name makes a sound.")
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val myAnimal = new Animal()
    myAnimal.speak()
  }
}

Extending with `extends`

Now, let's create a more specific animal, a Dog. A Dog is an Animal, so it can inherit from the Animal class using the extends keyword.

We can also override the speak method to give Dog its own unique sound. Notice the override keyword.

class Animal {
  val name: String = "Generic Animal"
  def speak(): Unit = {
    println(s"$name makes a sound.")
  }
}

class Dog extends Animal {
  override val name: String = "Dog"
  override def speak(): Unit = {
    println(s"$name barks!")
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val myAnimal = new Animal()
    val myDog = new Dog()
    myAnimal.speak() // Generic Animal makes a sound.
    myDog.speak()    // Dog barks!
  }
}

The Power of `override`

The override keyword is crucial in Scala for several reasons:

  • Clarity: It explicitly tells other developers that this method or field is replacing one from a superclass.
  • Safety: The compiler checks if you are actually overriding an existing member. If you make a typo or the superclass method changes, the compiler will alert you.
  • Intent: It clearly states your intention to specialize or change inherited behavior.

Always use override when redefining inherited members!

Polymorphism: Many Forms

Polymorphism means 'many forms'. In OOP, it allows objects of different classes to be treated as objects of a common superclass.

This means you can have a variable of type Animal that holds a Dog object, a Cat object, or any other subclass of Animal. When you call a method on that variable, the correct method for the actual object's type is executed.

  • Enables flexible and extensible code.
  • Allows a single interface for different data types.
  • Achieved through method overriding.

Polymorphism in Practice

Let's see polymorphism in action. We'll create a Cat class similar to Dog. Then, we can put different types of animals into a list of their common supertype, Animal, and call their speak method.

Notice how each animal speaks in its own unique way, even though they are all treated as Animal types in the list.

class Animal {
  val name: String = "Generic Animal"
  def speak(): Unit = {
    println(s"$name makes a sound.")
  }
}

class Dog extends Animal {
  override val name: String = "Dog"
  override def speak(): Unit = {
    println(s"$name barks!")
  }
}

class Cat extends Animal {
  override val name: String = "Cat"
  override def speak(): Unit = {
    println(s"$name meows!")
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val animals: List[Animal] = List(
      new Animal(),
      new Dog(),
      new Cat()
    )

    animals.foreach(_.speak())
  }
}

Introducing Abstract Classes

Sometimes, you want a base class that defines a common interface but doesn't provide a complete implementation for some of its members. This is where abstract classes come in.

An abstract class:

  • Cannot be instantiated directly (you can't create an object of an abstract class).
  • Can contain abstract methods (methods without an implementation) and concrete methods (methods with an implementation).
  • Serves as a blueprint that its subclasses must complete.

Building Abstract Blueprints

Let's define an abstract class Shape. Every shape has an area, but the formula for calculating it differs for each specific shape (circle, rectangle, etc.).

So, we declare area as an abstract method. Notice it has no body (no = { ... }). Subclasses will be responsible for providing its implementation.

abstract class Shape {
  def area: Double // Abstract method, no implementation
  
  def describe(): Unit = {
    println("This is a geometric shape.")
  }
}

// You cannot do: new Shape() - it's abstract!

object Main {
  def main(args: Array[String]): Unit = {
    println("Abstract class Shape defined.")
    println("Cannot instantiate Shape directly.")
  }
}

Concrete Implementations

Now, let's create concrete subclasses like Circle and Rectangle that extend our abstract class Shape. These subclasses must provide an implementation for the abstract area method.

They also inherit the concrete describe() method from Shape.

abstract class Shape {
  def area: Double
  def describe(): Unit = {
    println("This is a geometric shape.")
  }
}

class Circle(radius: Double) extends Shape {
  override def area: Double = math.Pi * radius * radius
}

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

object Main {
  def main(args: Array[String]): Unit = {
    val circle = new Circle(5)
    val rectangle = new Rectangle(4, 6)

    println(s"Circle area: ${circle.area}")
    circle.describe()
    println(s"Rectangle area: ${rectangle.area}")
    rectangle.describe()
  }
}

Inheritance & Polymorphism Check

Which of the following statements about inheritance and polymorphism in Scala are TRUE?

Recap: Inheritance & Polymorphism

Great job! In this lesson, we explored core OOP concepts in Scala:

  • Inheritance: How subclasses reuse code and define an 'is-a' relationship with superclasses using extends.
  • override Keyword: Essential for explicitly redefining inherited methods and fields, ensuring type safety and clarity.
  • Polymorphism: The ability to treat objects of different subclasses as objects of a common superclass, enabling flexible and extensible code.
  • Abstract Classes: Blueprints that can't be instantiated directly, used to define common interfaces and abstract members that subclasses must implement.

These concepts are vital for building well-structured and maintainable Scala applications!

Frequently asked questions

Is the “Inheritance and Polymorphism” lesson free?

Yes — the full text of “Inheritance and Polymorphism” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 2 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.

What will I learn in “Inheritance and Polymorphism”?

Explore object-oriented principles like inheritance, abstract classes, and polymorphism in Scala. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Scala for Backend Engineering & Functional Programming?

No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 2, so you can start here or from the beginning and move at your own pace.

How long does the “Inheritance and Polymorphism” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?

Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Classes, Objects, and Packaging
  2. Inheritance and Polymorphism
← Back to Scala for Backend Engineering & Functional Programming