What is Abstract in Java?
In Java, the abstract keyword is used to define an abstract class or an abstract method. An abstract class is a class that cannot be instantiated directly; it serves as a blueprint for other classes, and its main purpose is to provide a common interface and shared functionality for its subclasses. An abstract method is a method that is declared without an implementation in an abstract class, leaving the implementation to be provided by its subclasses.
Here are the key points to understand about abstract in Java:
- Abstract Class:
- An abstract class is declared using the abstract keyword in its class declaration.
- It may have both abstract and concrete methods (i.e., methods with an implementation).
- An abstract class cannot be instantiated directly using the new keyword. Instead, it is meant to be subclassed, and objects of its subclasses can be created.
- Abstract classes can have constructors, instance variables, and non-abstract methods, just like regular classes.
- If a class contains at least one abstract method, the class itself must be declared as abstract.
- Abstract classes are used to provide a common set of methods and attributes that must be implemented by its subclasses.
Example of an abstract class:
abstract class Shape { // Concrete method public void displayInfo() { System.out.println("This is a shape."); } // Abstract method without implementation public abstract double calculateArea(); }
- Abstract Method:
- An abstract method is declared using the abstract keyword in its method signature, and it does not have a method body (no curly braces).
- It is meant to be implemented in the subclass.
- Any class that contains one or more abstract methods must be declared as an abstract class.
Example of an abstract method in the abstract class Shape:
abstract class Shape { // ... // Abstract method without implementation public abstract double calculateArea(); }
Subclassing Abstract Classes:
- When you create a subclass of an abstract class, you must provide implementations for all the abstract methods declared in the superclass. Otherwise, the subclass must also be declared as abstract.
- Once all abstract methods are implemented in the subclass, you can instantiate objects of the subclass.
Example of a subclass that extends the abstract class Shape
:
class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public double calculateArea() { return Math.PI * radius * radius; } }
Explanation:
-
- Class Declaration:
- We have a class named Circle that extends (inherits from) the Shape This means Circle is a subclass, and Shape is the superclass.
- The use of extends Shape in the class declaration indicates that Circle inherits the attributes and methods of the Shape
- Instance Variable:
- The Circle class has a private instance variable radius, which represents the radius of the circle.
- The radius variable will be specific to each Circle object, and it is used to calculate the area of the circle in the calculateArea()
- Constructor:
- The class Circle has a constructor that takes a parameter radius.
- The constructor is responsible for initializing the radius instance variable with the value passed as an argument to the constructor.
- Method Override:
- The Circle class overrides the calculateArea() method inherited from the Shape
- The @Override annotation indicates that the method is an override of a superclass method, and it helps the compiler catch errors if the method signature doesn’t match any method in the superclass.
- The implementation of calculateArea() in the Circle class calculates the area of the circle using the formula: PI * radius * radius.
The Circle class is a specific type of Shape that represents a circle. By extending the Shape class, it inherits the common attributes and methods defined in the Shape class. Additionally, it provides its own specific implementation of the calculateArea() method, which is specific to circles.
This concept demonstrates how inheritance allows us to create specialized classes (subclasses) that inherit common behavior from a more general class (superclass). It promotes code reuse and supports a hierarchy of classes with shared functionalities and specific implementations.
- Class Declaration:
let’s provide another example of an abstract class and its subclass:
Suppose we have an abstract class Animal, which defines a method makeSound() as an abstract method. The abstract class Animal will serve as a base class for different types of animals. We will then create a subclass Dog that extends the Animal class and provides an implementation for the makeSound() method specific to dogs.
// Abstract class - Animal abstract class Animal { private String species; public Animal(String species) { this.species = species; } public String getSpecies() { return species; } // Abstract method - to be implemented by subclasses public abstract void makeSound(); } // Subclass - Dog (inherits from Animal) class Dog extends Animal { private String breed; public Dog(String species, String breed) { super(species); this.breed = breed; } public String getBreed() { return breed; } @Override public void makeSound() { System.out.println("Dog barks."); } } public class Main { public static void main(String[] args) { Dog dog = new Dog("Dog", "Labrador"); System.out.println("Species: " + dog.getSpecies()); System.out.println("Breed: " + dog.getBreed()); dog.makeSound(); } }
Output:
Species: Dog
Breed: Labrador
Dog barks.
Explanation:
-
- We define an abstract class Animal with a private attribute species and an abstract method makeSound().
- The Dog class is a subclass of Animal, and it extends it using the extends
- The Dog class provides its own implementation for the abstract makeSound() method, which prints “Dog barks.”
- In the main method, we create a Dog object and call its methods (getSpecies(), getBreed(), and makeSound()) to demonstrate the inheritance and method overriding behavior.
In this example, the abstract class Animal acts as a template for different types of animals, and the Dog class, as a subclass, provides the specific implementation for the abstract method makeSound(). This allows us to define common attributes and behavior in the abstract class and tailor the behavior to each subclass.
Let’s provide another example of an abstract class in Java:
// Abstract Class abstract class Shape { String color; Shape(String color) { this.color = color; } // Abstract method (no method body) abstract double calculateArea(); // Concrete method (has a method body) void displayInfo() { System.out.println("This is a " + color + " shape."); } } // Subclass implementing the abstract class class Circle extends Shape { double radius; Circle(String color, double radius) { super(color); // Call the constructor of the superclass with the 'color' parameter this.radius = radius; } @Override double calculateArea() { return Math.PI * radius * radius; } } // Subclass implementing the abstract class class Rectangle extends Shape { double length; double width; Rectangle(String color, double length, double width) { super(color); // Call the constructor of the superclass with the 'color' parameter this.length = length; this.width = width; } @Override double calculateArea() { return length * width; } }
Explanation:
- We have an abstract class Shape, which serves as a generic representation of shapes. It declares an abstract method calculateArea(), which is meant to be implemented by its subclasses. The calculateArea() method does not have a method body; it only provides a method signature.
- The Shape class also has a concrete method displayInfo(), which has a method body. Concrete methods in an abstract class can be used as is or overridden by subclasses.
- We define a subclass Circle, which extends the Shape abstract class. The Circle class provides a concrete implementation for the calculateArea() method and overrides the displayInfo() method to provide specific behavior for circles.
- The Circle class calculates the area of the circle using the formula π * r^2, where ‘r’ is the radius of the circle. It also overrides the displayInfo() method to indicate the color of the circle.
- We also define another subclass Rectangle, which extends the Shape abstract class. The Rectangle class provides a concrete implementation for the calculateArea() method and overrides the displayInfo() method to provide specific behavior for rectangles.
- The Rectangle class calculates the area of the rectangle using the formula length * width. It also overrides the displayInfo() method to indicate the color of the rectangle.
Now, let’s use the Shape, Circle, and Rectangle classes in the main method:
public class Main { public static void main(String[] args) { Circle circle = new Circle("red", 5.0); circle.displayInfo(); // Output: "This is a red shape." double circleArea = circle.calculateArea(); System.out.println("Area of the circle: " + circleArea); // Output: "Area of the circle: 78.53981633974483" Rectangle rectangle = new Rectangle("blue", 4.0, 6.0); rectangle.displayInfo(); // Output: "This is a blue shape." double rectangleArea = rectangle.calculateArea(); System.out.println("Area of the rectangle: " + rectangleArea); // Output: "Area of the rectangle: 24.0" } }
Explanation:
- In the main method, we create objects of the Circle and Rectangle classes, which are subclasses of Shape. The Circle and Rectangle classes provide concrete implementations for the abstract calculateArea() method inherited from the Shape abstract class.
- We call the displayInfo() method on each object. Since both Circle and Rectangle classes override the displayInfo() method, the specific implementation of the method in each subclass is executed, indicating the color of the shape.
- We also call the calculateArea() method on each object to calculate the area of the circle and rectangle using the concrete implementations provided by the Circle and Rectangle classes, respectively. The results are printed to the console.
This example demonstrates how an abstract class (Shape) provides a common structure and behavior that can be shared by its subclasses (Circle and Rectangle). Each subclass must implement its own version of the abstract method calculateArea(), making the design more flexible and extensible. The abstract class allows us to define a set of common methods that must be implemented by each subclass, promoting code reusability and creating a consistent and structured class hierarchy.
Why use an abstract class?:
Abstract classes are used for several reasons in Java and other object-oriented programming languages. Here are some key reasons to use abstract classes:
- To Define a Common Interface: Abstract classes are used to define a common interface or contract for a group of related classes. They can contain both abstract and concrete methods, allowing you to specify a set of methods that must be implemented by their subclasses.
- To Provide Default Implementations: Abstract classes can provide default implementations for some methods. Subclasses can choose to use or override these implementations as needed. This helps in sharing common functionality among related classes without duplicating code.
- To Enforce Method Implementation: By declaring abstract methods, abstract classes enforce that their subclasses must implement these methods. This ensures that the required behavior is defined in all subclasses, promoting consistency and adherence to the class contract.
- To Establish Class Hierarchies: Abstract classes play a central role in building class hierarchies. They act as base or parent classes for more specific subclasses. This hierarchy allows for a clear representation of different levels of abstraction in your application.
- To Encapsulate Common Behavior: Abstract classes can encapsulate common behavior and attributes shared among related classes. This avoids code redundancy and makes maintenance easier.
- To Define Template Methods: Abstract classes can include template methods, which are methods with a defined algorithm that use abstract methods as steps. Subclasses can provide implementations for the abstract methods, allowing them to customize the algorithm while following the overall structure defined in the template method.
- To Facilitate Polymorphism: Abstract classes are essential for polymorphism. Since they define a common interface, you can use a reference of the abstract class type to refer to objects of different subclasses. This allows for more flexible and generic code.
- To Prevent Instantiation: Abstract classes cannot be instantiated directly, which means you cannot create objects of an abstract class using the
new
keyword. This is useful when you want to prevent users from creating instances of a base class and instead require them to use subclasses.
In summary, abstract classes are a powerful tool for building class hierarchies, enforcing method implementations, and providing a common interface for related classes. They facilitate code reusability, promote good software design practices, and help you structure your code in a way that reflects the relationships between different types of objects in your application.