Java Interfaces and Functional Interfaces
Question
Explain the concept of interfaces in Java, including functional interfaces, default methods, and static methods. What are their uses, benefits, and best practices?
Answer
Interfaces in Java provide a way to achieve abstraction and define a contract that classes must implement. Functional interfaces are a special type of interface that contains exactly one abstract method, making them suitable for lambda expressions.
Basic Interface
public interface Drawable {
void draw(); // Abstract method
// Default method (Java 8+)
default void resize() {
System.out.println("Resizing...");
}
// Static method (Java 8+)
static void clear() {
System.out.println("Clearing...");
}
}
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing Circle");
}
}
Functional Interfaces
-
Basic Functional Interface
@FunctionalInterface public interface Predicate<T> { boolean test(T t); } // Usage with lambda Predicate<String> isEmpty = str -> str.isEmpty();
-
Common Functional Interfaces
// Consumer - takes input, returns nothing @FunctionalInterface public interface Consumer<T> { void accept(T t); } // Function - takes input, returns output @FunctionalInterface public interface Function<T, R> { R apply(T t); } // Supplier - takes nothing, returns output @FunctionalInterface public interface Supplier<T> { T get(); } // Runnable - takes nothing, returns nothing @FunctionalInterface public interface Runnable { void run(); }
Default Methods
-
Basic Default Method
public interface Vehicle { void start(); default void stop() { System.out.println("Vehicle stopped"); } } public class Car implements Vehicle { @Override public void start() { System.out.println("Car started"); } // stop() method is inherited }
-
Multiple Inheritance Resolution
public interface A { default void method() { System.out.println("A"); } } public interface B { default void method() { System.out.println("B"); } } public class C implements A, B { @Override public void method() { A.super.method(); // Explicitly call A's method B.super.method(); // Explicitly call B's method } }
Static Methods
-
Interface Static Methods
public interface MathOperations { static int add(int a, int b) { return a + b; } static int subtract(int a, int b) { return a - b; } } // Usage int sum = MathOperations.add(5, 3);
-
Utility Methods
public interface StringUtils { static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } static String reverse(String str) { return new StringBuilder(str).reverse().toString(); } }
Private Methods (Java 9+)
public interface Logger {
default void logInfo(String message) {
log("INFO", message);
}
default void logError(String message) {
log("ERROR", message);
}
private void log(String level, String message) {
System.out.println(level + ": " + message);
}
}
Interface Inheritance
-
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"); } }
-
Interface Extension
public interface Animal { void eat(); } public interface Pet extends Animal { void play(); } public class Dog implements Pet { @Override public void eat() { System.out.println("Dog is eating"); } @Override public void play() { System.out.println("Dog is playing"); } }
Best Practices
-
Interface Segregation
// Bad - Too many methods public interface Worker { void work(); void eat(); void sleep(); void play(); } // Better - Segregated interfaces public interface Workable { void work(); } public interface Eatable { void eat(); } public interface Sleepable { void sleep(); } public class Human implements Workable, Eatable, Sleepable { // Implement only needed methods }
-
Default Method Usage
public interface Collection<E> { int size(); boolean isEmpty(); default boolean isNotEmpty() { return !isEmpty(); } default boolean hasSize(int size) { return size() == size; } }
Common Use Cases
-
Callback Pattern
public interface Callback { void onSuccess(String result); void onError(Exception e); } public class AsyncTask { public void execute(Callback callback) { try { // Do something callback.onSuccess("Result"); } catch (Exception e) { callback.onError(e); } } }
-
Strategy Pattern
public interface PaymentStrategy { void pay(int amount); } public class ShoppingCart { private PaymentStrategy paymentStrategy; public void setPaymentStrategy(PaymentStrategy strategy) { this.paymentStrategy = strategy; } public void checkout(int amount) { paymentStrategy.pay(amount); } }
Modern Java Features
-
Sealed Interfaces (Java 17+)
public sealed interface Shape permits Circle, Rectangle { double getArea(); } public record Circle(double radius) implements Shape { @Override public double getArea() { return Math.PI * radius * radius; } }
-
Record Components in Interfaces
public interface Point { record Component(int x, int y) {} Component getPosition(); }
Common Pitfalls
-
Default Method Conflicts
public interface A { default void method() { System.out.println("A"); } } public interface B { default void method() { System.out.println("B"); } } public class C implements A, B { // Must override method to resolve conflict @Override public void method() { A.super.method(); } }
-
Interface Pollution
// Bad - Too many unrelated methods public interface Utility { void processData(); void formatText(); void calculateTax(); void sendEmail(); } // Better - Separate interfaces public interface DataProcessor { void processData(); } public interface TextFormatter { void formatText(); } public interface TaxCalculator { void calculateTax(); } public interface EmailSender { void sendEmail(); }