When to Use Java Interfaces Instead of Abstract Classes? A Complete Guide
This article explores three common doubts about Java interfaces, demonstrates how interface references can point to implementing objects, compares abstract classes and interfaces for achieving polymorphism, and provides detailed animal‑hierarchy examples with code, generics, and best‑practice recommendations.
1. Three Questions About Interfaces
Many beginners know that an interface can contain constants and abstract methods, and that a class can implement the interface to override those methods. However, they often wonder:
Why not write the methods directly in the class instead of creating an interface (or abstract class)?
If interfaces are similar to abstract classes, when should we choose an interface over an abstract class?
Why is it called an "interface" and how does that relate to hardware interfaces like USB?
2. Interface References Can Point to Implementations
An interface cannot be instantiated, but a reference of the interface type can hold an object of any class that implements it. B b = new A(); We can also cast an object to the interface type:
A a = new A90();
B b = (B) a;3. Abstract Classes for Polymorphism
The primary reason for using abstract classes is to achieve polymorphism. The following example defines an animal hierarchy.
Animal (abstract class)
Reptile (abstract, extends Animal)
Mammal (abstract, extends Animal)
Goat, Tiger, Rabbit (extend Mammal)
Snake (extends Reptile)
Farmer (no inheritance, can feed water to any Animal)
3.1 Animal Class
abstract class Animal {
public abstract String getName();
public abstract void move(String destination);
public abstract void drink();
}3.2 Mammal Class
abstract class Mammal extends Animal { }3.3 Reptile Class
abstract class Reptile extends Animal { }3.4 Tiger Class
class Tiger extends Mammal {
private static String name = "Tiger";
public String getName() { return this.name; }
public void move(String destination) { System.out.println("Goat moved to " + destination + "."); }
public void drink() { System.out.println("Goat lower its head and drink."); }
}3.5 Goat and Rabbit Classes
class Goat extends Mammal { /* similar to Tiger */ }
class Rabbit extends Mammal { /* similar to Tiger */ }3.6 Snake Class
class Snake extends Reptile {
private static String name = "Snake";
public String getName() { return this.name; }
public void move(String destination) { System.out.println("Snake crawled to " + destination + "."); }
public void drink() { System.out.println("Snake dived into water and drink."); }
}3.7 Farmer Class
class Farmer {
public void bringWater(String destination) { System.out.println("Farmer bring water to " + destination + "."); }
public void feedWater(Animal a) {
bringWater("Feeding Room");
a.move("Feeding Room");
a.drink();
}
public void feedAnimal(Animal ht, Animal a) {
bringWater("Feeding Room");
ht.move("Feeding Room");
((Huntable) ht).hunt(a);
}
}Running feedWater for a tiger, goat and snake shows how polymorphism lets the farmer treat all animals uniformly.
4. When Abstract Classes Aren’t Enough
If we need a method that only some subclasses should have (e.g., hunt), adding it to the abstract base class forces every subclass to implement it, wasting resources. An interface solves this by allowing only the relevant classes to implement the method.
5. Interfaces, Polymorphism, and Multiple Inheritance
Java does not support multiple class inheritance, but a class can implement multiple interfaces. By defining a Huntable interface, both Tiger and Snake can gain hunting behavior without altering the Animal hierarchy.
5.1 Huntable Interface
interface Huntable {
void hunt(Animal a);
}5.2 Tiger Implements Huntable
class Tiger extends Mammal implements Huntable {
// ... other methods ...
public void hunt(Animal a) { System.out.println("Tiger catched " + a.getName() + " and eated it"); }
}5.3 Snake Implements Huntable
class Snake extends Reptile implements Huntable {
// ... other methods ...
public void hunt(Animal a) { System.out.println("Snake coiled " + a.getName() + " and eated it"); }
}5.4 Updated Farmer
public void feedAnimal(Animal ht, Animal a) {
bringWater("Feeding Room");
ht.move("Feeding Room");
((Huntable) ht).hunt(a);
}This demonstrates how interfaces enable polymorphic behavior that abstract classes alone cannot provide.
6. Generic Interfaces
To make Huntable more flexible, we can add a generic type parameter:
interface Huntable<T> {
void hunt(T o);
}Then Tiger implements Huntable<Animal>, allowing the method to accept any type defined by the implementing class.
7. When to Prefer Interfaces Over Abstract Classes
When you need polymorphism across unrelated class hierarchies.
When the behavior is not a core attribute of the base class.
When multiple, unrelated classes should share the same method signature.
Examples include Comparable, which many different classes implement to enable sorting.
8. Why Are They Called “Interfaces”?
The term mirrors hardware interfaces (e.g., USB). Just as a USB port allows various devices to connect, a Java interface defines a contract that any class can “plug into” to provide the specified behavior, enabling true polymorphism.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
