DEV Community

Cover image for OOP Series: Abstraction and Interface In Java
Suleiman Ibrahim
Suleiman Ibrahim

Posted on

OOP Series: Abstraction and Interface In Java

INTRODUCTION

Abstraction is one of the four basic principles of Object-Oriented Programming (OOP). It enables us to give structure to our programs and applications. Abstract classes are used to create structure or design in which all other subclasses must follow.
Consider a smartphone, for example, smartphones have a screen that the user can touch to interact with the phone. Other properties of the smartphone are camera, speaker, battery, earphones, buttons, etc., and a smartphone can do some things like play music, make a call, receive a call, take pictures/ videos, etc. Considering some models of phones like Android, iOS, Blackberry, etc. An android phone, for instance, must at least be able to make a call, receive a call and browse the internet before we can consider it a smartphone.
For us to consider Android, iOS, and Blackberry in the class of phones, they must also be able to perform the basic functionalities listed above. Then can we conclude that we have abstracted Android and iOS from the Phone.

CONTENT

  • Prerequisite
  • Abstraction
  • Abstract class and Concrete class
  • Abstraction vs Encapsulation
  • Code Example
  • Interface
  • Properties of Interface
  • Code example
  • Conclusion

PREREQUISITE

ABSTRACTION

Abstraction in Java is the process of creating abstract classes and methods which define the structure of subclasses. We can achieve abstraction in Java by adding the keyword abstract to a class declaration name and providing abstract methods to the class.
It is also achieved using interfaces and abstract classes.

An abstract method is an instance method that is declared with the keyword abstract and does not have a body i.e. it is delimited by a semicolon.

public abstract void testMethod();
Enter fullscreen mode Exit fullscreen mode

Below are some of the properties of an abstract class:

  • You must declare it with the keyword abstract. Classes declared without the abstract keyword but containing at least one abstract method will throw a compilation error.
  • It cannot be instantiated. We cannot create objects of the class using the new keyword.
  • All subclasses inheriting from the abstract class must provide an implementation for the superclass abstract methods.
  • It can be sub-classed. Abstract classes can also act as a subclass and inherit the properties of a superclass.
  • It can contain zero or more abstract methods.
  • It can contain concrete methods. Concrete methods are methods with a body and implementation, unlike abstract methods that are delimited by a semicolon and do not provide an implementation.
  • All other members of the abstract class like instance variables and concrete methods continue to abide by the rules of inheritance. Subclasses handle them as they will for other concrete classes.

Abstract class and Concrete class

An abstract class is any class declared with the keyword abstract and contains some abstract methods. Some of the properties of the abstract class are listed above. A concrete class is somewhat opposite to an abstract class. A concrete class is a class that is not declared with the keyword abstract and does not contain any abstract method. While an abstract class cannot be instantiated, a concrete class can be instantiated.
Meanwhile, a concrete subclass inheriting from an abstract superclass must implement all of the superclass abstract methods otherwise the subclass must also be declared as an abstract class.

Abstraction Vs Encapsulation

You can achieve encapsulation without abstraction but cannot achieve abstraction without encapsulation.

Abstraction and encapsulation are two of the most misused concepts of OOP. Abstraction focuses on the design of the object in focus, while encapsulation focuses on the implementation.
The key to understanding the difference between the two is

Abstract all the things you need

and

Encapsulate all the things you don’t need.

Consider a car, for example, some of the basic necessities of a car are the steering wheel, accelerator pedal, brake pedal, gear selector, etc. Since a car needs all these things to be classified as a car, we can conclude that these things are being abstracted from the car. This is because a car can't do without them.
But the functionalities of these parts of the car are not made visible to the driver and the driver does not need to know how the steering wheel rotates the tire, or how the accelerator and brake pedal controls the speed of the car. This is known as encapsulation. The implementation and the details of these parts are hidden from the driver so the driver only interacts with the few parts made available by the car like the parts listed above. In this case, we are hiding the complexities from the driver, which is encapsulation.

Code Example

The three programs below explain the concept of abstraction in Java using three classes. The Phone abstract class and two other concrete classes - the Android class and IOS class.

The Phone Class

Phone class
The image depicts the Phone class hierarchy. The Phone class is blurred in the image to signify it is an abstract class.

package Abstraction;

public abstract class Phone {

    private String color;
    private String brand;

    // initialize instance variables
    public Phone(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    // set and get methods

    public void setColor(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    // abstract methods

    public abstract void makeCall();

    public abstract void receiveCall();

    public abstract void browseInternet();

    // overrides Object's method
    @Override
    public String toString() {
        return String.format("%s specs", getBrand());
    }
}

Enter fullscreen mode Exit fullscreen mode

The Phone class starts by adding an abstract class declaration in its signature with the keyword abstract. This is the default syntax for declaring a class as abstract in Java.
The class moves on to declare its instance variables and then to the constructor used to initialize these variables, followed by setters and getters methods used to access and modify these instance variables.
The next set of methods includes the abstract methods makeCall(), receiveCall(), and browseInternet() that demonstrates the must have of a smartphone. This is similar to the smartphones we use in real life, most smartphones perform these three basic functionalities. Every other type of phone inheriting the properties of our Phone class must be able to make a call, receive a call, and browse the internet. The implementation of these methods might vary, but functionality must be the same.
One thing to note here is that the Phone class contains concrete methods with implementations along with its abstract methods.
The next two classes will be inheriting these properties of the Phone class.

The Android Class

package Abstraction;

public class Android extends Phone {

    private String version; // android version

    public Android(String color, String brand, String version) {
        super(color, brand); // initialize superclass variables
        this.version = version; // initialize instance variable
    }

    // set and get methods

    public void setVersion(String version) {
        this.version = version;
    }

    public String getVersion() {
        return version;
    }

    // abstract methods implementation

    @Override
    public void makeCall() {
        System.out.println("Calling with Android ...");
        System.out.println("Call ended.");
    }

    @Override
    public void receiveCall() {
        System.out.println("Calling with Android ...");
        System.out.println("Call ended.");
    }

    @Override
    public void browseInternet() {
        System.out.println("Browsing with iPhone ...");
        System.out.println("Session ended.");
    }

    @Override
    public String toString() {
        return String.format("%s%nVersion: %s%nColor: %s%n",
                super.toString(), getVersion(), super.getColor());
    }
}
Enter fullscreen mode Exit fullscreen mode

The Android class like any other regular subclass class inherits from a superclass which is the Phone class using the keyword extends (similar to inheritance) in its class declaration. It includes an instance variable and a constructor to initialize the variable, then the setter and getter method to manipulate the variable.
Our focus here are the methods makeCall(), receiveCall(), and browseInternet(). They override the superclass methods using the @Override annotation. Here, the Android class must override these abstract methods and provide implementation (a body) in order to be called a concrete method. This is because the Phone class it is inheriting from contains these abstract methods.
This is very useful in designing smartphones in real life where each smartphone has a basic requirement they must meet. Because of these design constraints, users won't be overwhelmed by the variations of smartphone design each brand is producing.

The IOS Class

package Abstraction;

public class IOS extends Phone {

    private int camera; // number of camera

    public IOS(String color, String brand, int camera) {
        super(color, brand); // initialize superclass variables
        this.camera = camera; // initialize instance variable
    }

    // set and get methods

    public void setCamera(int camera) {
        this.camera = camera;
    }

    // get the number of camera
    public int getCamera() {
        return camera;
    }

    // abstract methods implementation

    @Override
    public void makeCall() {
        System.out.println("Calling with iPhone ...");
        System.out.println("Call ended.");
    }

    @Override
    public void receiveCall() {
        System.out.println("Receiving call with iPhone ...");
        System.out.println("Call ended.");
    }

    @Override
    public void browseInternet() {
        System.out.println("Browsing with iPhone ...");
        System.out.println("Session ended.");
    }

    // override Object's method

    @Override
    public String toString() {
        return String.format("%s%nColor: %s%nNumber of Camera: %d%n",
                super.toString(), super.getColor(), getCamera());
    }
}
Enter fullscreen mode Exit fullscreen mode

The IOS class above is similar in structure to the Android class. It contains an instance variable, a constructor to initialize the instance variable, and a setter and getter method to manipulate the instance variable.
The main difference between the Android class and the IOS class is the implementation of the inherited abstract methods. In this case, both have similar methods and prints different message which in real life it might be some block of code with actual functionalities. But for the sake of this article, we are keeping things simple by printing some text to the console. Now, if a user is to transition from an IOS to an Android device, they won't be much difference in the functionalities of the two devices. Because from the onset, both share the same design. This is another good advantage of abstraction in the real world.

The PhoneTest Class

package Abstraction;

public class PhoneTest {

    public static void main(String[] args) {

        // create objects
        Android android = new Android("Android", "Diamond Black", "8.9.9");
        IOS ios = new IOS("iOS", "Pale Blue", 5);

        // display objects
        System.out.println(android);
        System.out.println(ios);

        // perform action with objects
        android.makeCall();
        System.out.println("--------------------------------------"); // white space
        ios.receiveCall();
    }

}
Enter fullscreen mode Exit fullscreen mode

We use the PhoneTest Class above to test the implementation of the three classes created above - Phone, Android, and IOS class.
It starts by creating objects of both the Android and IOS classes and initializing their instance variables by passing the values into their constructor call.
The next set of statements display the string representation of the respective objects created above. Note that we printed the objects without any helper methods but rather with the help of the toString() method overridden inside each class. It helps to display objects of that class in a custom way.
The next statements perform some actions with the objects by making a call with the android object and then receiving a call with the ios object, and the implementation of each method is printed in the console.

Below is the output of executing the PhoneTest class.

PhoneTest output - abstraction

INTERFACE

Interface specifies what to do and not how to do it.

An interface in Java is a type that contains static constants and abstract methods and defines the behavior of classes. Interface is an extreme case of abstraction and is used to achieve 100% abstraction. This implies that interfaces cannot contain concrete methods and all the abstract methods it contains must be implemented by the concrete subclass.
Interface comes into play when classes that are not related need to share a common design. This happens when we need objects of different classes to respond to the same method call.
Interface in Java acts as an interface between different classes sharing a common design. Consider the TV remote control, for example, most remote control contains at least one button, so if a particular model/brand of remote control is to implement the remote interface, it must contain at least one button because the default design of a remote includes at least one button.

Properties of Interface

  • Similar to abstract classes, interfaces cannot be instantiated. An object of an interface cannot be created using the new keyword.
  • All fields in an interface are implicitly public, static, and final.
  • Interface cannot have concrete methods.
  • Interface cannot extend other classes.
  • All methods in an interface are implicitly public and abstract methods.
  • A class must implement all of the methods of an interface, otherwise it must be declared abstract.
  • Interface can extend (but cannot implement) other interfaces.
  • A class can implement multiple interfaces. This is done using a comma-separated list of interfaces after the keyword implements in the class declaration.

public class className extends superClassName implements interface1, interface2, interface3, ...

Code Example

The code snippets below explain the concept of interfaces in Java.

The Camera Class

package Abstraction;

public interface Camera {

    public void takePicture();

    public void flashlight();       

}
Enter fullscreen mode Exit fullscreen mode

The program above contains an interface Camera that has only two methods. As seen from the program, the class declaration includes the keyword interface they are no concrete methods here because interface in Java does not accept it. The methods that this interface has included some of the basic functionalities of a camera. So any class that wants to implement a Camera interface must be able to take a picture and includes a flashlight (at least).

Modification to the Android Class

The two snippets below modify the Android class to implement the Camera interface.

public class Android extends Phone implements Camera { 
Enter fullscreen mode Exit fullscreen mode

The snippet above implements the Camera interface using the Android class by adding the keyword implements followed by the name of the interface to implement, in this case; Camera is the interface to implement. This is the default syntax for implementing an interface in Java. In real life, for an Android phone to have a camera interface, it must be able to take pictures along with a flashlight, this is exactly what our Camera class is doing here - enforcing the Camera design on Android.

// overridden interface methods

    @Override 
    public void takePicture() {
        System.out.println("Taking picture ...");
        System.out.println("Picture taken");
    }

    @Override
    public void flashlight() {
        System.out.println("Flashlight is turned on ...");
        System.out.println("Flashlight is now off");
    }
Enter fullscreen mode Exit fullscreen mode

The snippet above shows another modification done to the Android class. All other parts of the program remain the same except for these two methods added to the class because of the interface the class is implementing.
The two methods above provide implementation to the takePicture() and flashlight() methods in the Camera interface. As seen from the properties of an interface, all of the methods must be implemented in the Android class else the compiler will throw an error.

        android.flashlight();
Enter fullscreen mode Exit fullscreen mode

The above line of code was added to the phone test class to demonstrate one of the methods the Android class is implementing.

Below is an output of running the PhoneTest class with the modifications done to the android class.
PhoneTest output - interface
The output above remains similar to the first output except for the last two lines that display the output of implementing the flashlight() method.

Conclusion

This article starts by introducing the concept of abstraction in Java and its properties as well. The knowledge of Abstraction is a very valuable concept in OOP and programming as a whole because of its numerous advantages and applications in the real world.

Interface, on the other hand, is an extreme case of abstraction. Most software engineers encourage the use of interface as it helps to preserve design and also helps in modeling software.
This article briefly introduces the concept of abstraction and interface in Java. Visit the official Java documentation to know more about the concepts discussed here.

Discussion (0)