Interfaces
Contracts for classes
Overview
An interface is a contract that defines what a class can do, without specifying how it does it. Interfaces contain method signatures (and constants) but no implementation. Classes that implement an interface must provide implementations for all its methods.
Interfaces are fundamental to achieving loose coupling and are essential for design patterns like Strategy, Factory, and Dependency Injection.
Key Concepts
Defines a contract (what, not how)
Contains abstract method signatures
A class can implement multiple interfaces
Enables polymorphism and loose coupling
Java 8+: Can have default and static methods
Code Example
1// Interface definition
2public interface PaymentProcessor {
3 boolean processPayment(double amount);
4 void refund(String transactionId);
5 PaymentStatus getStatus(String transactionId);
6}
7
8// Multiple implementations
9public class CreditCardProcessor implements PaymentProcessor {
10 @Override
11 public boolean processPayment(double amount) {
12 System.out.println("Processing credit card payment: $" + amount);
13 // Credit card specific logic
14 return true;
15 }
16
17 @Override
18 public void refund(String transactionId) {
19 System.out.println("Refunding to credit card: " + transactionId);
20 }
21
22 @Override
23 public PaymentStatus getStatus(String transactionId) {
24 return PaymentStatus.COMPLETED;
25 }
26}
27
28public class PayPalProcessor implements PaymentProcessor {
29 @Override
30 public boolean processPayment(double amount) {
31 System.out.println("Processing PayPal payment: $" + amount);
32 // PayPal specific logic
33 return true;
34 }
35
36 @Override
37 public void refund(String transactionId) {
38 System.out.println("Refunding via PayPal: " + transactionId);
39 }
40
41 @Override
42 public PaymentStatus getStatus(String transactionId) {
43 return PaymentStatus.COMPLETED;
44 }
45}
46
47// Client code - depends on interface, not implementation
48public class CheckoutService {
49 private final PaymentProcessor paymentProcessor;
50
51 // Dependency injection through constructor
52 public CheckoutService(PaymentProcessor paymentProcessor) {
53 this.paymentProcessor = paymentProcessor;
54 }
55
56 public void checkout(Cart cart) {
57 double total = cart.getTotal();
58 if (paymentProcessor.processPayment(total)) {
59 System.out.println("Checkout successful!");
60 }
61 }
62}
63
64// Usage - easy to switch implementations
65PaymentProcessor processor = new CreditCardProcessor();
66// Or: PaymentProcessor processor = new PayPalProcessor();
67CheckoutService checkout = new CheckoutService(processor);CheckoutService works with any PaymentProcessor implementation. Adding UPIProcessor requires no changes to CheckoutService.
When to Use
When you need multiple implementations
To define a contract between components
For dependency injection and loose coupling
When implementing design patterns
To enable mocking in unit tests
Advantages
- ✓
Loose coupling - classes depend on abstractions
- ✓
Multiple inheritance of type
- ✓
Easy to swap implementations
- ✓
Better testability with mocks
- ✓
Follows Dependency Inversion Principle
Best Practices
- 1.
Program to interface, not implementation
- 2.
Keep interfaces focused (Interface Segregation)
- 3.
Name interfaces after capabilities (Runnable, Comparable)
- 4.
Use interfaces for dependency injection
- 5.
Prefer interfaces over abstract classes for contracts
💡 Interview Tips
Explain difference between interface and abstract class
Show how interfaces enable loose coupling
Demonstrate with payment or notification example
Mention role in SOLID principles (DIP, ISP)
Discuss Java 8 default methods