Welcome to Abstraction

Welcome back! Previously, you delved into polymorphism and learned how to create more flexible code structures using classes and inheritance. In this session, we will take a step further and explore a crucial aspect of Object-Oriented Programming: Abstraction.

Understanding Abstract Classes and Abstract Methods

Abstract classes and abstract methods are essential tools for achieving abstraction. They allow you to define a common interface for a group of derived classes, ensuring that specific methods are implemented. This approach helps you write more robust and scalable programs.

1. Defining an Abstract Class

Let's revisit some of the key concepts through the following code example:

// Define an abstract class Shape. Note that the abstractness is achieved by having at least one abstract method.
abstract class Shape(private val color: String) {

    // Abstract methods for calculating the area and perimeter
    abstract fun area(): Double
    abstract fun perimeter(): Double

    // Concrete method to get the color
    fun getColor(): String {
        return color
    }
}

In this snippet, we define an abstract class Shape with a property color and a concrete method getColor to retrieve the color. The class also contains two abstract methods: area and perimeter. An abstract class can have properties and fully defined methods, but it must contain at least one abstract method, making it impossible to instantiate directly.

2. Implementing the Abstract Methods in Derived Classes

Next, we create concrete classes that extend the abstract class Shape.

Circle Class

// Define a Circle class that inherits from Shape
class Circle(private val radius: Double, color: String) : Shape(color) {

    // Implement the area and perimeter methods
    override fun area(): Double {
        return Math.PI * radius * radius
    }

    override fun perimeter(): Double {
        return 2 * Math.PI * radius
    }
}

Here, the Circle class inherits from Shape and provides concrete implementations for the abstract methods area and perimeter. It also includes a primary constructor to initialize the radius and color.

Rectangle Class

Let's look at one more example.

// Define a Rectangle class that inherits from Shape
class Rectangle(private val width: Double, private val height: Double, color: String) : Shape(color) {

    // Implement the area and perimeter methods
    override fun area(): Double {
        return width * height
    }

    override fun perimeter(): Double {
        return 2 * (width + height)
    }
}

Similarly, the Rectangle class inherits from Shape and implements the necessary methods area and perimeter. It also includes a constructor to initialize the width, height, and color, ensuring that all necessary attributes are properly initialized.

3. Using the Abstract Class and Derived Classes
Why It Matters

Abstract classes and abstract methods offer a way to enforce certain patterns and rules in your code. They allow you to design a system where different types of objects can be treated uniformly while ensuring that specific behaviors are implemented in each derived class.

By mastering abstract classes and abstract methods, you'll be able to:

  1. Create more organized and readable code: You'll have a clear structure that dictates how certain methods should behave.
  2. Encourage code reusability: Common code can reside in abstract base classes, reducing redundancy.
  3. Enhance flexibility: Easily add new types of derived classes without modifying existing code — a key principle of software design.

Intrigued? Let's move on to the practice section and solidify these concepts together. You're on your way to becoming proficient in building sophisticated and maintainable systems!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal