What is inheritance, and when does extending a class make sense rather than composing one?
Inheritance is a way for one type to acquire the state and behaviour of another and then specialise it. The child class is said to be a subclass of the parent, and it may add new members or override inherited ones. Java supports single class inheritance and multiple interface implementation, and it enforces that a subclass is substitutable for its parent at the type level.
public class Main {
public static void main(String[] args) {
Notification email = new EmailNotification("order shipped");
Notification sms = new SmsNotification("order shipped");
email.send();
sms.send();
}
}
abstract class Notification {
protected final String body;
Notification(String body) { this.body = body; }
abstract void send();
void log() { System.out.println("queued: " + body); }
}
class EmailNotification extends Notification {
EmailNotification(String body) { super(body); }
void send() { System.out.println("email -> " + body); }
}
class SmsNotification extends Notification {
SmsNotification(String body) { super(body); }
void send() { System.out.println("sms -> " + body); }
}Extending makes sense when the relationship really is a kind-of relationship and the shared behaviour is genuinely the same. Here every notification carries a body and knows how to send itself; what differs is only the delivery channel. The abstract send method makes the variation explicit, and the base class holds everything the subclasses share.
Composition is often the better tool. If you only need to reuse the logic rather than express a type relationship, hold the other object as a field and delegate to it. That keeps the classes independent, avoids a fragile base class, and lets you change the strategy at runtime rather than locking it in at compile time.
The rule of thumb to state out loud: extend when you can say the child is a parent without flinching, and compose when you can only say the child uses a parent.