Interfaces and Abstract Classes in Java
Question
Compare and contrast interfaces and abstract classes in Java. Explain their purposes, implementation requirements, and use cases. When would you choose one over the other?
Answer
Interfaces and abstract classes are both mechanisms for achieving abstraction in Java, but they serve different purposes and have distinct characteristics.
Abstract Classes
-
Basic Abstract Class
public abstract class Shape { protected double area; protected String color; public Shape(String color) { this.color = color; } // Abstract method public abstract double calculateArea(); // Concrete method public void displayInfo() { System.out.println("Color: " + color); System.out.println("Area: " + area); } } public class Circle extends Shape { private double radius; public Circle(String color, double radius) { super(color); this.radius = radius; } @Override public double calculateArea() { return Math.PI * radius * radius; } }
-
Partial Implementation
public abstract class Database { protected Connection connection; public void connect() { // Common connection logic connection = establishConnection(); } protected abstract Connection establishConnection(); public abstract void disconnect(); } public class MySQLDatabase extends Database { @Override protected Connection establishConnection() { // MySQL-specific connection return DriverManager.getConnection("jdbc:mysql://..."); } @Override public void disconnect() { // MySQL-specific disconnection connection.close(); } }
Interfaces
-
Basic Interface
public interface Drawable { void draw(); double getArea(); void setColor(String color); } public class Rectangle implements Drawable { private double width; private double height; private String color; @Override public void draw() { System.out.println("Drawing rectangle"); } @Override public double getArea() { return width * height; } @Override public void setColor(String color) { this.color = color; } }
-
Multiple Interface Implementation
public interface Flyable { void fly(); } public interface Swimmable { void swim(); } public class Duck implements Flyable, Swimmable { @Override public void fly() { System.out.println("Duck is flying"); } @Override public void swim() { System.out.println("Duck is swimming"); } }
Key Differences
-
Constructor Support
// Abstract class can have constructors public abstract class Vehicle { protected String model; public Vehicle(String model) { this.model = model; } } // Interface cannot have constructors public interface Vehicle { // No constructors allowed }
-
Method Implementation
// Abstract class can have both abstract and concrete methods public abstract class PaymentProcessor { public abstract void processPayment(double amount); public void validateAmount(double amount) { if (amount <= 0) { throw new IllegalArgumentException("Invalid amount"); } } } // Interface methods are implicitly abstract (except default methods) public interface PaymentProcessor { void processPayment(double amount); // Cannot have concrete methods without 'default' keyword }
Modern Java Features
-
Default Methods in Interfaces
public interface Logger { void log(String message); default void logError(String message) { log("ERROR: " + message); } default void logInfo(String message) { log("INFO: " + message); } }
-
Private Methods in Interfaces
public interface StringProcessor { default String process(String input) { return format(validate(input)); } private String validate(String input) { if (input == null || input.trim().isEmpty()) { throw new IllegalArgumentException("Invalid input"); } return input.trim(); } private String format(String input) { return input.toUpperCase(); } }
Best Practices
-
Choosing Between Interface and Abstract Class
// Use abstract class when you have common implementation public abstract class Database { protected Connection connection; public void connect() { // Common connection logic } protected abstract void configureConnection(); } // Use interface when you need to define a contract public interface PaymentMethod { void processPayment(double amount); boolean validatePayment(); String getPaymentDetails(); }
-
Interface Segregation
// Bad - Too many methods in one interface public interface Worker { void work(); void eat(); void sleep(); void commute(); } // Good - Segregated interfaces public interface Workable { void work(); } public interface Eatable { void eat(); } public interface Sleepable { void sleep(); } public class Employee implements Workable, Eatable, Sleepable { // Implement only needed methods }
Common Use Cases
-
Template Method Pattern
public abstract class Game { public final void play() { initialize(); startGame(); endGame(); } protected abstract void initialize(); protected abstract void startGame(); protected abstract void endGame(); } public class Chess extends Game { @Override protected void initialize() { // Chess-specific initialization } @Override protected void startGame() { // Chess-specific game start } @Override protected void endGame() { // Chess-specific game end } }
-
Strategy Pattern
public interface PaymentStrategy { void pay(double amount); } public class CreditCardPayment implements PaymentStrategy { @Override public void pay(double amount) { // Credit card payment implementation } } public class PayPalPayment implements PaymentStrategy { @Override public void pay(double amount) { // PayPal payment implementation } }
Testing
-
Testing Abstract Classes
@Test public void testShape() { Shape circle = new Circle("red", 5.0); circle.calculateArea(); circle.displayInfo(); assertEquals("red", circle.color); assertTrue(circle.area > 0); }
-
Testing Interfaces
@Test public void testDrawable() { Drawable rectangle = new Rectangle(10, 20); rectangle.setColor("blue"); rectangle.draw(); assertEquals(200.0, rectangle.getArea()); }