What is Abstract in Java?
In Java, “abstract” is a keyword and a concept used to define abstract classes and methods. An abstract class is a class that cannot be instantiated directly and is often used as a blueprint for other classes to inherit from. Abstract methods are methods declared in abstract classes but do not have a method body; instead, they are meant to be implemented by the subclasses that inherit from the abstract class.
Here’s an explanation of “abstract” in Java:
Abstract Classes:
- An abstract class is a class that cannot be instantiated on its own, meaning you cannot create objects directly from it. It serves as a template or a partially complete class designed for other classes to inherit from.
- Abstract classes are created using the abstract keyword, and they often contain a mix of concrete (fully implemented) methods and abstract (unimplemented) methods.
- Abstract classes allow you to define a common structure and behavior for a group of related classes. Subclasses that inherit from an abstract class must provide implementations for the abstract methods, making them more specific and complete.
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 a method declared in an abstract class without providing a method body. It’s defined using the abstract keyword and is meant to be overridden and implemented by subclasses.
- Subclasses that inherit from an abstract class must provide concrete implementations for all the abstract methods declared in the superclass. This enforces a consistent interface and behavior for subclasses.
- Abstract methods enable polymorphism, where objects of different subclasses can be treated uniformly through a reference to the abstract class. The specific behavior is determined by the subclass at runtime.
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:
- when a class, like “Circle,” is declared to “extend” another class, such as “Shape,” it signifies that “Circle” inherits properties and methods from “Shape.” “Circle” is then considered a subclass, and “Shape” becomes its superclass. This relationship allows “Circle” to access and utilize the attributes and behaviors defined in “Shape,” promoting code reuse and hierarchy in object-oriented programming.
- 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 in the Circle class is responsible for setting the radius attribute to the value provided as an argument when creating a Circle object. This initialization ensures that each Circle instance can have its own unique radius, allowing you to work with circles of different sizes. This is a fundamental part of object-oriented programming, as it encapsulates the state of each circle and ensures that it starts with the desired radius value, making the class versatile and adaptable for various use cases.
- 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 exemplifies a specific category of shapes, namely circles, and it benefits from the properties and behaviors shared by all shapes through its inheritance from the Shape class. This inheritance mechanism allows the Circle class to access and utilize common attributes and methods defined in the Shape class, promoting code reusability and providing a foundation for specialized, circle-specific functionality. 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). inheritance promotes efficient code reuse and enables the creation of a class hierarchy. It allows for the sharing of common functionalities among related classes, while also facilitating the implementation of class-specific behaviors. This enhances code organization and reduces redundancy, making it easier to maintain and extend Java applications.
- Class Declaration:
Use Cases:
- Abstract classes are useful when you want to create a common structure for a group of related classes, ensuring they share certain methods or attributes.
- Abstract methods allow you to define a contract that must be fulfilled by subclasses, ensuring that specific behaviors are implemented consistently across subclasses.
- Abstract classes and methods are often used in design patterns and frameworks to provide a foundation for customization.
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.Next, we’ll extend the Animal class to create a subclass called Dog, which offers a dog-specific implementation of the makeSound() function.
// 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().
- Basically, the Dog class here is a subclass of Animal, and it extends it using the extends keyword
- 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().
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 create a subclass called
Circle
, which extends an abstract class namedShape
. 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.
In this example, an abstract class, “Shape,” acts as a blueprint for its subclasses, “Circle” and “Rectangle.” Abstract classes provide a common structure and shared behavior, ensuring that each subclass implements its own version of the abstract method “calculateArea.” This design enhances flexibility and extensibility.
By using an abstract class, we establish a set of required methods that each subclass must implement. This approach fosters code reusability, maintains a structured class hierarchy, and enforces consistent behavior among related classes.
Why use an abstract class?:
Using an abstract class in Java offers several benefits and serves various purposes, making it a valuable tool in object-oriented programming. Here’s a unique explanation of why you might use an abstract class:
- Blueprint for Subclasses: Abstract classes provide a blueprint for creating related subclasses. They define a common structure and set of methods that subclasses must implement, ensuring that each derived class adheres to a specific design and interface.
- Code Reusability: Abstract classes promote code reusability. By encapsulating common behavior in the abstract class, you avoid duplicating code in multiple subclasses. This saves development time and makes code easier to maintain.
- Enforce Method Implementation: Abstract classes can define abstract (unimplemented) methods that must be overridden by subclasses. This enforces a contract, ensuring that specific behavior is implemented consistently across all derived classes.
- Polymorphism: Abstract classes support polymorphism, allowing objects of different subclasses to be treated uniformly through a reference to the abstract class. This promotes flexibility and extensibility in your code.
- Frameworks and Design Patterns: Abstract classes are often used in software frameworks and design patterns to provide a foundation for customization. They enable developers to extend and adapt framework components while maintaining a standardized interface.
- Encapsulation: Abstract classes encapsulate related data and behavior in a single unit. This helps manage complexity, improves organization, and enhances the understanding of code.
- Consistency and Structure: By defining a common structure through abstract classes, you establish a consistent and structured class hierarchy. This makes it easier to navigate and maintain your codebase, especially in larger projects.
- Flexibility for Future Enhancements: Abstract classes allow you to add new methods and behavior to your class hierarchy in the future without affecting existing subclasses. This makes your code more adaptable to changing requirements.
In summary, abstract classes are a powerful tool in Java that facilitates code organization, reusability, and the creation of well-defined class hierarchies. They are especially valuable when you have a group of related classes that share common characteristics but require individualized behaviors in specific areas.