Page Content

Tutorials

What is Abstraction in Java with an Example?

Abstraction in Java

Object-oriented programming (OOP) relies on abstraction to hide implementation details and reveal only necessary functionality or features. It simplifies complexity by focussing on what objects do rather than how they do it. Java uses abstract classes and interfaces.

Abstract Classes

Abstract classes use the abstract keyword. Its main aim is to provide a general design that all its subclasses can use, leaving the specifics and implementations to them.

Defining and Using Abstract Classes

A class can have abstract and concrete methods. As a blueprint, it defines some methods and leaves others to subclasses.

Take, for instance, a Figure abstract class that offers a common structure for two-dimensional shapes but is unable to implement area() in a useful way due to the shape’s undefined status.

// Using abstract methods and classes.
abstract class Figure {
  double dim1;
  double dim2;
  Figure(double a, double b) { // Constructor 
    dim1 = a;
    dim2 = b;
  }
  // area is now an abstract method 
  abstract double area(); // Signature without implementation 
  void showDim() { // Concrete method 
    System.out.println("Dimensions: " + dim1 + " and " + dim2);
  }
}

Abstract Methods: Signature Without Implementation

An abstract method has no method body or implementation and is declared using the abstract modifier. It is composed of a signature (name, return type, and parameter list) and a semicolon. Any class that has one or more abstract methods needs to be declared abstract.

An abstract class’s subclasses must either declare themselves abstract or implement all of its abstract methods. Eventually, the full implementation will be provided by a concrete (non-abstract) subclass.

Cannot Instantiate Abstract Classes

Abstract classes can’t be instantiated with new. Abstract class objects are “useless” because they lack comprehensive method implementations because they are not fully defined. To achieve runtime polymorphism, you can establish a reference variable of an abstract class type to refer to its concrete subclasses.

Consider concrete subclasses Rectangle and Triangle extending Figure:

class Rectangle extends Figure {
  Rectangle(double a, double b) {
    super(a, b);
  }
  @Override // Overriding the abstract area() method 
  double area() {
    System.out.print("Inside Area for Rectangle. ");
    return dim1 * dim2;
  }
}
class Triangle extends Figure {
  Triangle(double a, double b) {
    super(a, b);
  }
  @Override // Overriding the abstract area() method 
  double area() {
    System.out.print("Inside Area for Triangle. ");
    return dim1 * dim2 / 2;
  }
}
class AbstractAreas {
  public static void main(String args[]) {
    // Figure f = new Figure(10, 10); // Illegal: Cannot instantiate abstract class!
    Rectangle r = new Rectangle(9, 5);
    Triangle t = new Triangle(10, 8);
    Figure figref; // This is OK, no object is created 
    figref = r;
    System.out.println("Area is " + figref.area()); // Calls Rectangle's area() 
    figref = t;
    System.out.println("Area is " + figref.area()); // Calls Triangle's area() 
  }
}

Code Output:

Inside Area for Rectangle. Area is 45.0
Inside Area for Triangle. Area is 40.0

This illustrates how the relevant area() method is called polymorphically and that Figure cannot be created, but its reference can point to concrete subclasses. An “is-a” relationship is the main function of abstract classes (for example, a Rectangle is a Figure).

Abstract Interfaces

Like classes, interfaces are reference types that function as collections of constant values and abstract methods. Java achieves full abstraction using interfaces.

Defining an Interface: Implicitly Public, Static, Final Members

Declare an interface with interface. Interface methods were implicitly public and abstract before JDK 8. Their bodies are absent, and they conclude with a semicolon.

It is necessary to initialise variables that are declared inside an interface since they are implicitly public, static, and final (constants).

public interface MyConstants {
    // These are implicitly public static final constants
    int MIN_VALUE = 0;
    String ERROR_MESSAGE = "Operation not supported";
    // This is an implicitly public abstract method 
    void performAction();
}

Interfaces, like abstract classes, are not directly instantiable. Classes must put them into practice. A “has-a” capacity is established via interfaces (e.g., an object has the capability to do a given action).

Implementing Interfaces

To use an interface, a class must implement it using the implements keyword in its declaration. When a class implements an interface, it is essentially agreeing to supply concrete implementations for every abstract method that the interface declares. A class must declare itself abstract if none of its methods are implemented.

One of the main benefits of interfaces is that a class can implement more than one interface. This offers an alternative to Java classes, which do not natively support multiple inheritance of type or behaviour.

Extending Interfaces

By employing the extends keyword, interfaces can also extend other interfaces. Every method (and constant) of the parent interface is passed down to the child interface. Every method in the whole inheritance chain must have an implementation provided by the class implementing such a child interface.

Default Methods in Interfaces (JDK 8+)

Default methods are a major improvement that was introduced in JDK 8 and give an interface method an implementation (a method body). Using the default keyword, a default method is declared.

When interfaces evolved, the main reason for default methods was to provide binary compatibility. All classes that implemented an interface would break if a new method was added before JDK 8 since they would suddenly have to implement the new method. By offering a default implementation that current classes can inherit without alteration, default methods address this issue and preserve binary compatibility.

public interface Printable {
    // Abstract method (implicitly public abstract)
    void printContent(String content);
    // Default method (JDK 8+), provides a default implementation 
    default void printString() {
        System.out.println("Default implementation: Nothing to print.");
    }
}
public class WithDefault implements Printable {
    @Override
    public void printContent(String content) {
        System.out.println("Printing: " + content);
    }
    // No need to implement printString(), it uses the default
}
public class OverrideDefault implements Printable {
    @Override
    public void printContent(String content) {
        System.out.println("Overriding printContent for: " + content);
    }
    @Override // Can override the default method 
    public void printString() {
        System.out.println("Overridden implementation: Custom string printed!");
    }
}
class DefaultMethodDemo {
    public static void main(String[] args) {
        WithDefault obj1 = new WithDefault();
        obj1.printContent("Hello World");
        obj1.printString(); // Uses the default implementation
        OverrideDefault obj2 = new OverrideDefault();
        obj2.printContent("Java is powerful");
        obj2.printString(); // Uses the overridden implementation
    }
}

Code Output:

Printing: Hello World
Default implementation: Nothing to print.
Overriding printContent for: Java is powerful
Overridden implementation: Custom string printed!

Interfaces cannot have instance variables or state information, even with default methods, which distinguishes them from classes. Beginning in JDK 8, interfaces can define implicitly public static methods that provide an implementation but are not inherited by implementing classes or subinterfaces.

Index