Introduction
Composition and inheritance are two fundamental approaches to code reuse in Java. While both allow sharing behavior, knowing when to prefer composition is crucial for building flexible, maintainable software.
Why It Matters
- Impacts code maintainability and flexibility.
- Influences class design and hierarchy depth.
- Prevents tight coupling and promotes reusability.
When to Use
- Inheritance: When there is a clear "is-a" relationship and behavior is stable.
- Composition: When you want to reuse functionality without creating rigid hierarchies.
Core Concepts
What is Inheritance?
- A mechanism to acquire properties and behavior from a parent class.
- Achieved using
extends
keyword.
class Vehicle {
void move() { System.out.println("Vehicle moving"); }
}
class Car extends Vehicle {
void honk() { System.out.println("Car honking"); }
}
What is Composition?
- Building classes using references to other classes (has-a relationship).
- Achieved via member objects.
class Engine {
void start() { System.out.println("Engine starting"); }
}
class Car {
private Engine engine = new Engine();
void drive() { engine.start(); }
}
Real-World Analogy
- Inheritance: A sports car is a type of car.
- Composition: A car has an engine.
Comparison Table
Aspect | Inheritance | Composition |
---|---|---|
Relationship | Is-a | Has-a |
Flexibility | Rigid hierarchy | High flexibility |
Code Reuse | Via superclass | Via contained objects |
Coupling | Tight | Loose |
Overriding | Possible | Requires delegation |
Real-World Use Cases
-
Inheritance:
- GUI frameworks (JFrame extends Container).
- Domain hierarchies like
Animal -> Dog
.
-
Composition:
- Car with engine.
- Order with payment service.
- Decorator design pattern.
Common Mistakes & Anti-Patterns
- Overusing Inheritance:
- Leads to deep, fragile hierarchies.
- Violating Liskov Substitution Principle:
- Subclasses that don’t behave like parent classes.
- Forgetting Delegation in Composition:
- Wrapping without exposing necessary behavior.
Performance & Memory Implications
- Both have minimal direct performance impact.
- Composition provides more runtime flexibility by allowing swapping of components.
- Inheritance creates stronger compile-time binding.
Best Practices
- Favor composition for code reuse unless a true is-a relationship exists.
- Keep inheritance hierarchies shallow.
- Use interfaces with composition for polymorphism.
- Apply design patterns like Strategy and Decorator with composition.
Java Version Relevance
Version | Change |
---|---|
Java 8+ | Lambdas make composition more powerful with functional interfaces |
Java 14+ | Records promote composition with immutability |
Code Example: Prefer Composition
interface PaymentProcessor {
void process(double amount);
}
class CreditCardProcessor implements PaymentProcessor {
public void process(double amount) {
System.out.println("Processing credit card payment: " + amount);
}
}
class OrderService {
private final PaymentProcessor processor;
OrderService(PaymentProcessor processor) {
this.processor = processor;
}
void checkout(double amount) {
processor.process(amount);
}
}
- OrderService uses composition with PaymentProcessor, allowing easy swapping of implementations.
Conclusion & Key Takeaways
- Inheritance is for "is-a" relationships; composition is for "has-a".
- Prefer composition to avoid rigid hierarchies and tight coupling.
- Use delegation with composition to expose required behavior.
FAQ
-
What is composition in Java?
Using member objects to build functionality (has-a relationship). -
What is inheritance?
Sharing behavior via parent-child classes (is-a relationship). -
Why prefer composition over inheritance?
It provides more flexibility and reduces coupling. -
Can I mix composition and inheritance?
Yes, use both when appropriate. -
Does composition affect performance?
Minimal, often negligible compared to design benefits. -
Is delegation required in composition?
Yes, to expose wrapped behavior. -
What’s a common anti-pattern with inheritance?
Deep class hierarchies and misuse of is-a relationships. -
How does composition help testing?
Allows mocking/swapping dependencies easily. -
Can interfaces be used with composition?
Yes, to achieve polymorphism. -
Which design patterns use composition?
Strategy, Decorator, Adapter, Composite.