Abstract Classes
In object-oriented design, base classes are often defined as abstract to serve as generic templates for a hierarchy of subclasses. As one moves up the inheritance tree, classes become more generalized. For instance, a specific breed inherits from a broader category like Canine, which in turn inherits from Animal. Since the higher-level classes represent abstract concepts rather than concrete objects, Java prevents the instantiation of abstract classes. While an Animal class cannot represent a specific entity, its subclasses like Dog or Cat can.
To declare an abstract class, the abstract keyword is used. Methods within this class can also be marked as abstract, indicating they have no implementation body and must be overridden by subclasses. An abstract method serves no purpose on its own and relies on inheritance to provide functionality. If a class contains even a single abstract method, the class itself must be declared abstract.
public abstract class Component {
abstract void initialize(); // Defining an abstract method
}
Any class extending an abstract parent is required to implement all inherited abstract methods. In a polymorphic scenario, one might define a base class as abstract and declare a method like draw() as abstract, forcing every subclass to provide its specific drawing logic.
However, this approach has limitations. If a subclass logically belongs to the hierarchy but does not require the specific functionality (e.g., a 3D model in a 2D drawing framework), it is still forced to implement the method, leading to redundant or empty code blocks. Furthermore, Java classes support single inheritance, preventing a subclass from inheriting from multiple parents to mix different functionalities. To address these constraints, interfaces are used.
Interfaces
An interface acts as a contract or a pure abstract class. It defines a set of behaviors that a class agrees to implement. To solve the issue mentioned above, functionality like draw() can be moved to an interface. Classes that need this functionality implement the interface, while still inheriting from their base abstract class. This decouples the specific behavior from the inheritance hierarchy.
Interfaces are defined using the interface keyword. Methods within an interface are implicitly public and abstract, and variables are implicitly public, static, and final.
public interface Renderable {
void render(); // Method is implicitly public and abstract
}
A class implements an interface using the implements keyword. A class can extend one parent class and implement multiple interfaces.
public class Cube extends Shape implements Renderable {
// Class implementation
}
The following example demonstrates how different geometric shapes implement a rendering interface while sharing a common base class:
interface Renderable {
void render();
}
// Base abstract class
abstract class Shape {
abstract double getArea();
}
// Concrete class implementing the interface
class Circle extends Shape implements Renderable {
public void render() {
System.out.println("Rendering Circle");
}
public double getArea() {
return 0.0; // Simplified for demo
}
}
// Another concrete class implementing the interface
class Rectangle extends Shape implements Renderable {
public void render() {
System.out.println("Rendering Rectangle");
}
public double getArea() {
return 0.0; // Simplified for demo
}
}
// Class extending base but not implementing the interface
class Polygon extends Shape {
public double getArea() {
return 0.0; // Simplified for demo
}
}
public class ShapeDemo {
public static void main(String[] args) {
// Polymorphism: treating objects as the interface type
Renderable[] renderList = {
new Circle(),
new Rectangle()
};
for (int i = 0; i < renderList.length; i++) {
renderList[i].render();
}
}
}
In this example, Circle and Rectangle inherit from Shape and implement Renderable. The renderList array holds objects of the interface type Renderable. When the loop calls render(), the JVM invokes the specific implementation corresponding to the actual object type (upcasting). The Polygon class remains a standard Shape without being forced to implement the rendering logic.
Interfaces and Inheritance
While Java forbids multiple inheritance for classes (a class cannot extend more than one class), it allows a class to implement multiple interfaces. This enables a form of multiple inheritance where a class can acquire behaviors from several sources. The syntax involves listing interfaces after the implements keyword, separated by commas.
class ClassName implements InterfaceOne, InterfaceTwo, InterfaceThree {
// Implement all methods from all interfaces
}
Additionally, interfaces themselves can inherit from other interfaces using the extends keyword, allowing for the creation of hierarchical behavior contracts.
interface BaseInterface {
void baseMethod();
}
interface ExtendedInterface extends BaseInterface {
void extendedMethod();
}