Scala Inheritance, Polymorphism

Learn via video courses
Topics Covered

Overview

Scala combines functional and object-oriented programming paradigms seamlessly. Polymorphism and inheritance in Scala provide powerful tools for designing flexible and maintainable software. Inheritance enables the creation of new classes that inherit properties and behaviors from existing ones, promoting code reuse and hierarchy in Scala. Polymorphism in Scala allows objects of different classes to respond to the same method or message in a way that suits their types, enhancing code flexibility.

Introduction

Polymorphism and inheritance are fundamental concepts in object-oriented programming, and Scala provides a rich and expressive environment for their implementation. Polymorphism allows objects of different classes to be treated as instances of a common superclass, promoting flexibility and code reusability. Inheritance, on the other hand, enables the creation of new classes that inherit properties and behaviors from existing ones, forming a hierarchy of relationships. In Scala, these concepts are seamlessly integrated, offering powerful ways to design and structure code for a wide range of applications, from small scripts to large-scale software systems.

What is Inheritance in Scala?

Inheritance in Scala is a fundamental object-oriented programming (OOP) concept that allows us to create new classes (derived or subclass) based on an existing class (base or superclass). The derived class inherits the properties (fields) and behaviors (methods) of the base class.

Implementation

In Scala, we can implement inheritance using the extends keyword to create a subclass (derived class) that inherits properties and behaviors from a superclass (base class).

  • Here, the Dog class extends the Animal class, inheriting the speak method.

Example

Output:

  • In the given example, inheritance is used to create a new class Dog that inherits properties and methods from an existing class Animal. This allows us to reuse and extend the behavior of Animal for dogs.
  • The Dog class inherits the name property and speak method from Animal while adding its own wagTail method. This demonstrates the concept of code reuse and specialization, where the subclass (Dog) builds upon the functionality of the superclass (Animal).

Types of Inheritance in Scala.

Single

Single inheritance is a fundamental form of inheritance found in most object-oriented programming languages, including Scala. In single inheritance, a class can inherit properties and methods from only one superclass (base class).

Single inheritance

Example:

  • In this example, the Dog class inherits from the Animal class, demonstrating single inheritance. The Dog class can access the eat method from the Animal class.

Multiple

Scala does not support multiple inheritance in the traditional sense, where a class can inherit from multiple superclasses. However, it achieves a similar effect through multiple inheritance using traits. Traits are similar to interfaces in other languages but can also contain concrete method implementations. A class can mix in multiple traits to inherit from multiple sources.

multiple-inheritances

Example:

  • In this example, the Bird class mixes in both the Swimmer and Flyer traits, allowing it to inherit and use the swim and fly methods.

Multilevel

Multilevel inheritance refers to a situation where a class inherits from another class, which in turn inherits from another class. This forms a chain of inheritance. Scala supports multilevel inheritance like many other object-oriented languages.

multilevel

Example:

  • In this example, the Child class inherits from the Parent class, which, in turn, inherits from the Grandparent class. The Child class can access methods from both the Parent and Grandparent classes.

Hierarchical

Hierarchical inheritance occurs when multiple classes inherit from a single superclass. This creates a hierarchical structure where different classes share common behavior from a common base class.

Hierarchical

Example:

  • In this example, both the Dog and Cat classes inherit from the Animal class. They share the eat method from the common base class.

Hybrid

Hybrid inheritance is a combination of different types of inheritance mentioned above, such as single, multiple, multilevel, or hierarchical inheritance. Scala supports hybrid inheritance, allowing us to create complex class hierarchies that combine various inheritance forms.

hybrid

Example:

  • In this example, the Dolphin class inherits from the Animal class using single inheritance and mixes in the Swimmer trait using multiple inheritance. This demonstrates a hybrid inheritance scenario.

What is Polymorphism in Scala?

Polymorphism in Scala refers to the ability of different objects or classes to respond to the same method or message in a way that is specific to their individual types. It allows us to write code that can work with objects of different classes, treating them as instances of a common superclass or interface.

In Scala, polymorphism is a key feature of object-oriented programming and plays a crucial role in building flexible and reusable code.

Implementation

Polymorphism in Scala is implemented primarily through method overriding, which allows different classes to respond to the same method in a type-specific way.

Example

Output:

  • We define a Animal superclass with a makeSound method that returns a generic sound.
  • The Dog and Cat subclasses extend Animal and override the makeSound method to provide their specific sounds.
  • In the PolymorphismExample object, we create instances of Dog and Cat and store them in variables of type Animal.
  • We call the makeSound method on these variables, and polymorphism comes into play. Even though the variables are of type Animal, Scala dynamically dispatches the appropriate method based on the actual type of the objects they reference. This results in "Dog barks" for myPet1 and "Cat meows" for myPet2.

Principle Form of Polymorphism

Subtyping

Subtyping in Scala is a form of run-time polymorphism, also known as dynamic polymorphism. It allows objects of derived classes (subtypes) to be treated as objects of their base class (supertype) at runtime.

  • Dynamic Dispatch: Polymorphism through subtyping allows method calls to be dynamically dispatched based on the actual type of the object at runtime. This is also known as dynamic polymorphism or late binding.
  • Inheritance Required: Subtyping typically involves inheritance, where a subclass extends a superclass. The subclass inherits properties and behaviors from the superclass.
  • Implementation: In Scala, method overriding is a common way to achieve subtyping. A subclass can override a method inherited from its superclass, providing its own implementation while still being treated as an instance of the superclass.

Example:

  • In this example, the speak method in the Dog class overrides the method in the Animal class, and polymorphism allows us to call the method on an Animal reference, which is actually referencing a Dog object.

Generics

Generics, also known as parametric polymorphism, is a form of polymorphism that allows code to be written in a way that can work with different data types while providing compile-time type safety.

  • Static Type Checking: Generics are resolved at compile time, ensuring that type-related errors are caught at compile time rather than at runtime. This is also known as static polymorphism or early binding.
  • Type Parameterization: In Scala, generics are achieved through type parameters. We can create classes, methods, or traits that accept type parameters, which are placeholders for actual types.
  • Implementation: A common use of generics in Scala is defining generic classes and methods. For example, we can create a generic container class that can hold values of any type, with the type parameter determined at compile time.

Example:

  • In this example, the Box class is generic and can hold values of any type T. The type parameter T is resolved at compile time. This allows us to create instances of Box with different types, providing compile-time type checking and safety.

Type of Polymorphism in Scala.

Method Overriding

Method overriding allows a subclass to provide its own implementation for a method that is already defined in its superclass. When a method is called on an object of a subclass, the overridden version of the method in the subclass is executed instead of the one in the superclass.

  • The appropriate method to call is determined at runtime based on the actual type of the object. This is also known as dynamic polymorphism or late binding.
  • Method overriding typically involves inheritance, where a subclass extends a superclass and provides a specific implementation for a method defined in the superclass.
  • In Scala, we can use the override keyword to indicate that a method in a subclass is intended to override a method in the superclass. The overridden method is called when the method is invoked on an object of the subclass.

Example:

Method Overloading

Method overloading is a form of polymorphism in which multiple methods with the same name but different parameter lists (types or number of parameters) are defined within a class or object.

  • The appropriate method to call is determined at compile time based on the method signature and the provided arguments. This is also known as static polymorphism or early binding.
  • Overloaded methods must have the same name but different parameter lists, which may include differences in the number or types of parameters.
  • In Scala, method overloading is achieved by defining multiple methods with the same name but different parameters. The compiler determines which method to call based on the arguments provided when the method is invoked.

Example:

Conclusion

  • In Scala, inheritance is a mechanism that allows a class to inherit properties and behaviors (methods and fields) from another class, facilitating code reuse and the creation of class hierarchies.
  • Polymorphism in Scala is the ability of objects of different classes to respond to the same method or message in a way specific to their individual types, promoting flexibility and code reusability.