Java > Object-Oriented Programming (OOP) > Polymorphism > Abstract Classes and Interfaces
Polymorphism with Abstract Classes and Interfaces: Shape Example
This example demonstrates polymorphism using abstract classes and interfaces in Java. It shows how different shapes (Circle, Rectangle) can implement a common interface (Shape) or extend an abstract class (AbstractShape) and be treated uniformly through a common type.
The Shape Interface
The Shape
interface defines the contract that all shapes must adhere to. It declares two methods: area()
and perimeter()
. Any class implementing this interface must provide implementations for these methods.
public interface Shape {
double area();
double perimeter();
}
The Circle Class
The Circle
class implements the Shape
interface. It provides concrete implementations for the area()
and perimeter()
methods specific to a circle, using its radius. The @Override
annotation indicates that these methods are implementing methods from the interface.
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
public double getRadius() {
return radius;
}
}
The Rectangle Class
The Rectangle
class also implements the Shape
interface, providing its own specific implementations for area()
and perimeter()
based on its length and width.
public class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
@Override
public double perimeter() {
return 2 * (length + width);
}
public double getLength() {
return length;
}
public double getWidth() {
return width;
}
}
Demonstrating Polymorphism
This Main
class demonstrates polymorphism. We create instances of Circle
and Rectangle
and assign them to variables of type Shape
. We can then call the area()
and perimeter()
methods on these variables, and the correct implementation will be executed based on the actual type of the object at runtime. The loop iterates through an array of Shape
objects, and the correct area()
and perimeter()
methods are called for each shape, regardless of whether it's a circle or a rectangle. This is the essence of polymorphism.
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
System.out.println("Circle Area: " + circle.area());
System.out.println("Circle Perimeter: " + circle.perimeter());
System.out.println("Rectangle Area: " + rectangle.area());
System.out.println("Rectangle Perimeter: " + rectangle.perimeter());
Shape[] shapes = new Shape[2];
shapes[0] = circle;
shapes[1] = rectangle;
for (Shape shape : shapes) {
System.out.println("Area: " + shape.area());
System.out.println("Perimeter: " + shape.perimeter());
}
}
}
Abstract Shape Class
An abstract class, AbstractShape
, is defined with a name and abstract methods for area()
and perimeter()
. Abstract classes cannot be instantiated directly but can be subclassed.
public abstract class AbstractShape {
private String name;
public AbstractShape(String name) {
this.name = name;
}
public abstract double area();
public abstract double perimeter();
public String getName() {
return name;
}
}
Circle Extending AbstractShape
The CircleExtendsAbstract
class extends AbstractShape
and provides concrete implementations for the area()
and perimeter()
methods, similar to the Circle
class implementing the Shape
interface.
public class CircleExtendsAbstract extends AbstractShape {
private double radius;
public CircleExtendsAbstract(String name, double radius) {
super(name);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
public double getRadius() {
return radius;
}
}
Concepts Behind the Snippet
Circle
and Rectangle
can be treated as Shape
objects.
Real-Life Use Case
Consider a graphics rendering engine. You might have different shapes (circles, squares, triangles) that all need to be drawn on the screen. Using polymorphism, you can treat all these shapes as a generic 'Drawable' object and call the 'draw' method on each, without needing to know the specific type of shape. The correct drawing implementation will be called based on the object's actual type.
Best Practices
Interview Tip
Be prepared to explain the difference between interfaces and abstract classes. Interfaces define a contract, while abstract classes provide a partial implementation. A class can implement multiple interfaces but can only inherit from one abstract class.
When to Use Them
Memory Footprint
Interfaces themselves do not directly contribute to memory footprint at runtime. However, classes implementing an interface will have the memory footprint associated with the methods that are implemented. Abstract classes, on the other hand, have a small overhead compared to interfaces due to the potential for instance variables and concrete methods.
Alternatives
Instead of interfaces, you can use abstract classes or concrete classes (though less flexible). Instead of abstract classes, you can use interfaces with default method implementations (Java 8 and later).
Pros
Cons
FAQ
-
What is the difference between an abstract class and an interface?
An abstract class can have both abstract and concrete methods, while an interface can only have abstract methods (until Java 8, which introduced default methods in interfaces). A class can inherit from only one abstract class but can implement multiple interfaces. -
Why use polymorphism?
Polymorphism allows you to write code that can work with objects of different classes in a uniform way. This promotes code reusability and flexibility. -
Can an interface extend another interface?
Yes, an interface can extend one or more other interfaces. This allows you to create more complex interfaces by combining simpler ones.