Abstract Classes and Interfaces in Java: Abstraction and Flexibility

Hello everyone, 🖖🏼

In this article, I would like to address the topic of Abstract and Interface, which has often confused me and made it difficult to understand which one to use and why. I will provide a detailed explanation of what these classes are, why they are used, and the differences between them. I hope you find this article informative and enjoyable.

Happy reading! 🎇

 

In Java, Abstract and Interface classes are two distinct structures that enable a strong representation of the abstraction concept in software design. Both are abstract classes and cannot be instantiated directly, meaning they cannot be created as objects. However, these structures exhibit differences and offer unique advantages based on the scenarios in which they are used.

Abstract classes are used to define and share common properties among concrete classes through their abstraction capabilities. They serve as an ideal tool to group classes with common characteristics in a class hierarchy and pass on these characteristics through inheritance.

Abstract classes can combine abstract and concrete methods to specify the methods that must be implemented by subclasses. This increases code reusability and ensures interoperability.

On the other hand, Interface classes are used as a kind of contract to define common behaviors or functionality among various classes. They determine which methods a class must implement, serving as a guideline. Interfaces in Java enable multiple inheritance by allowing a class to implement multiple interfaces, thus leveraging the flexibility advantage offered by multiple inheritance. They contribute to making the code more flexible, extensible, and easy to maintain.

Now that we have taken a brief look at both concepts, we can delve into these concepts in more detail. 🐱‍🏍

Abstract Classes

Abstraction is used to represent complex systems in a simpler and more understandable way in the real world. For example, let’s consider a car and the common features that can exist in all cars. Fuel consumption, speed, braking system, number of doors, or color are some of the features that can be found in every car. We can take these common features as a foundation and create a model. With this foundation, we can then differentiate and customize the features to create cars with different characteristics. Essentially, what we are doing here is abstraction, and we use this abstract class to create new classes.

Abstract classes are also used in a similar manner. We can create an abstract class with the basic features defined. By deriving different brands and models of cars from this abstract class, we can customize them accordingly.

Abstract classes are abstract structures that define the properties (instance variables, etc.) and behaviors of an abstract class. These classes can be inherited and used by other concrete classes. One of the key features that differentiates abstract classes from interfaces is that they can contain both abstract and concrete methods. The abstract methods they contain are defined by the classes that inherit the abstract class, providing flexibility in the written code.

Let’s create an example abstract class.

abstract class Shape {
   protected String color;
   
   public Shape(String color) {
      this.color = color;
   }
   
   public abstract double area();
   
   public void display() {
      System.out.println("Bu bir " + color + " şekildir.");
   }
}

In this example, we have created an abstract class called “Shape”. It encapsulates the common property of Shape, which is the “color” variable, and the “display” method. Additionally, the “area” method is defined as abstract, which means that each subclass inheriting from this class can override the “area” method according to its own implementation.

Now, let’s create two different classes that inherit from the abstract class “Shape” and examine their usage.

class Circle extends Shape {
   private double radius;
   
   public Circle(String color, double radius) {
      super(color);
      this.radius = radius;
   }
   
   public double area() {
      return Math.PI * radius * radius;
   }
}

// -----------

class Rectangle extends Shape { 
   private double length;
   private double width;
   
   public Rectangle(String color, double length, double width) {
      super(color);
      this.length = length;
      this.width = width;
   }
   
   public double area() {
      return length * width;
   }
}

When the classes Rectangle and Circle inherit from the abstract class Shape, they acquire the properties defined in this class. However, if desired, they can also add their own properties on top of these inherited properties. For example, the Circle class may have a “radius” property, and the Rectangle class may have a “width” property.

IS-A Relationship

Here, there is another point I would like to address: the is-a relationship. Abstract classes and the classes inheriting from those abstract classes establish an is-a relationship. To briefly illustrate this relationship using our scenario as an example,

The Rectangle is-a shape. The Circle is-a shape.

So, Rectangle and Circle is a Shape and possesses all the properties of Shape.

Real Life Examples

Alright, everything is fine, but how and when will I use these abstract classes in real-life projects? Now, let me try to provide some small examples about that.

  • For example, let’s consider that we are developing an e-commerce application. In this application, we can create an abstract class that represents the general characteristics of products. For instance, we can create an abstract class named ‘Product’ to define the common attributes of products. Subclasses can represent different product categories and define their own customized methods using the properties of the abstract class.

  • Alternatively, let’s imagine that we are designing a banking application. Here, we can utilize an abstract class that represents different types of bank accounts. For example, by creating an abstract class named ‘BankAccount’, we can define common attributes such as account number and balance. Subclasses can define methods and operations specific to different account types (e.g., current account, savings account, credit card account).

Summary

Let’s try to summarize the whole topic in bullet points, considering the real-life examples we have discussed:

  • Abstract classes can contain both methods and variables.
  • They cannot be instantiated directly, meaning they cannot be called with the ’new()’ keyword.
  • They can be inherited.
  • They can contain both abstract and concrete methods.
  • They have constructors.
  • They establish an “is-a” relationship with the classes that inherit from them.

Now let’s move on to our next concept, Interface classes.

Interface Classes

Interface classes are a construct used in the Java programming language to define related methods and behaviors. An interface allows a class to implement a specific behavior or fulfill an interface. This enables Java to support multiple inheritance and allows different classes to have similar behavior.

An interface only defines the signatures of methods (parameter list and name) without specifying the implementation details. This means that interfaces can be thought of as a contract or agreement between classes. When a class implements an interface, it is obligated to implement all the methods defined in that interface.

The author of the book ‘Learning Java’ has made a great analogy about interfaces.

Interfaces are kind of like Boy Scout or Girl Scout merit badges. A scout who has learned to build a birdhouse can walk around wearing a little sleeve patch with a picture of one. This says to the world, “I know how to build a birdhouse.” Similarly, an interface is a list of methods that define some set of behavior for an object. Any class that implements each method listed in the interface can declare at compile time that it implements the interface and wear, as its merit badge, an extra type — the interface’s type.

CAN-DO Relationship

A similar relationship to the is-a relationship mentioned in abstract classes exists here as well. I believe that these relationships contribute to a clearer and easier understanding of these concepts.

In the case of interface classes, a can-do relationship is established between the interface and the classes implementing it. We can think of it as having a certain skill. When a class implements an interface, it can possess the abilities defined by that interface.

To illustrate with a small example,

WhatsApp can do voice call

Let’s imagine in this example that we have an interface class named “Phone” and a subclass named “WhatsApp” where the “WhatsApp” class implements the “Phone” interface. Let’s put this into code💻

public interface Phone(){
    void voiceCall();
    void textMessage(String message);
}

Here, we have created a “Phone” interface and specified its methods. Now, let’s create a class named “WhatsApp” and utilize this interface.

public class WhatsApp() implements Phone{
   private String logoColor;
   
   public WhatsApp(String color){
      this.logoColor = color;
   }

   @Override
   public void voiceCall(){
      System.out.println("Starting voice call via WhatsApp");
   }

    @Override
   public void textMessage(String message){
      System.out.println(message + " message sent via WhatsApp");
   }
}

In this example, by implementing the Phone interface, we have added the voiceCall() and textMessage() functionalities to the WhatsApp class.

So far, we have only used a single interface, but Java supports multiple interface inheritance. This means that a class can implement multiple interfaces. Now, let’s enhance our previous example to demonstrate the usage of multiple interfaces.

public interface Phone {
    void makeCall(String phoneNumber);
    void sendMessage(String message);
}

public interface Camera {
    void takePhoto();
    void recordVideo();
}

I have created two separate interface classes, Phone and Camera. Now let’s implement them in the WhatsApp class.

public class Whatsapp implements Phone, Camera {
   private String logoColor;
   
   public WhatsApp(String color){
      this.logoColor = color;
   }

   @Override
   public void makeCall(String phoneNumber) {
        System.out.println(phoneNumber+" calling via WhatsApp");
   }

   @Override
   public void sendMessage(String message) {
        System.out.println(message+" was sent via WhatsApp");
   }

   @Override
   public void takePhoto() {
        System.out.println("Photo taking via WhatsApp");
   }

   @Override
   public void recordVideo() {
        System.out.println("Video recording via WhatsApp");
   }
}

As we saw in our example, by implementing two different interfaces in the WhatsApp class, we provided the class with different capabilities.

Now that we have covered the examples, I believe we have understood everything. However, let’s explore a few more examples to see how we can apply these concepts in our real projects.

Real Life Examples

  • this time in the context of e-commerce. Suppose we want to integrate a payment system. We can create a common interface called PaymentGateway. Whenever we want to integrate a new payment system into the system, each payment system would implement the PaymentGateway interface and implement its own payment methods. Another example could be related to shipping options. We can think of different classes representing various shipping providers, and we can create a common interface for them. Each shipping provider would implement this common interface and carry out the shipping operations.

  • Another example could be an hotel management system. We can create an interface called Room to represent different room types. Each room type would implement the Room interface and handle operations such as room reservation and retrieving room information.

So far, we have examined both concepts separately and provided examples. Now, let’s combine what we have learned and write a sample code that uses both concepts(abstract+interface) together.

// Abstract
abstract class Animal {
    abstract void sound();
}

// Interface
interface Swimable {
    void swim();
}


// The Duck class that inherited from 
// Animal and implemented from Swimmable
class Duck extends Animal implements Swimable {
    @Override
    void sound() {
        System.out.println("Duck sound..");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming..");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.sound(); 
        duck.swim();   
    }
}

In this example, we created an abstract class named Animal and an interface named Swimable. Then, we wrote the Duck class, which inherits from the Animal class and implements the Swimable interface. The Duck class fulfills the requirements of the sound method from the abstract class and the swim method from the interface. Thus, we have seen that in Java, a class can inherit from an abstract class and implement an interface at the same time.

Now that we have covered the concept of interfaces, let’s summarize everything we have discussed in brief bullet points.

  • Interfaces only contain method signatures, not method implementations.
  • They establish a can-do relationship with the classes that implement them.
  • A class can inherit from multiple interfaces.
  • By default, all interface members are implicitly abstract and public. You don’t need to explicitly specify it.
  • Interfaces specify what a class should do, not how it should do it.
  • Like abstract classes, interface classes cannot be instantiated using the “new” keyword.

Interface vs. Abstract Classes

Before we reach the end of this article, let’s take a bird’s-eye view of everything we’ve covered and prepare a comparative table that can serve as a summary of the entire article.

Concept Abstract Class Interface Class
Content Methods and variables Only methods
Object Creation Cannot be instantiated Cannot be instantiated
Inheritance Can be inherited Can be implemented
Method Types Both concrete and abstract methods Only abstract methods
Constructor Present Absent
Relationship is-a relationship Can-do relationship
Multiple Implementation Not supported Supported

Thank you for taking the time to read this article. I have tried to provide information about Interface and Abstract classes to the best of my ability. I hope it has been a helpful piece of writing.

Happy Coding🖖🏼

REFERENCES