Inheritance permits a subclass to extend a class; overriding supplies a different implementation of an inherited instance method.
Java inheritance and method overriding
Working model
Use inheritance when the subtype must satisfy the same contract. Extending a class just to reuse a helper couples unrelated behavior and can expose operations the subtype should not support.
Code
public class DeliveryCharge {
public int chargePaise() {
return 5000;
}
static class PickupCharge extends DeliveryCharge {
@Override
public int chargePaise() {
return 0;
}
}
public static void main(String[] args) {
DeliveryCharge charge = new PickupCharge();
System.out.println(charge.chargePaise());
}
}The output is 0. The reference type permits the call; the object’s runtime class supplies the overridden implementation. The annotation catches a mismatched signature at compilation.
Cost and design choices
Prefer composition when pricing policies should be replaceable independently of the object hierarchy. A contained policy object makes that dependency explicit.
Substitution has behavioral limits
A subclass inherits more than names: callers already have assumptions about the parent’s accepted inputs, results, and state changes. A child that unexpectedly rejects valid parent inputs or changes units breaks those assumptions even if the code compiles.
Prefer composition when the new type needs to use another object without promising that it can replace that object. Inherited mutable protected fields can make invariants harder to control because several layers can alter them.
Connected lessons
Continue with Java interfaces and replaceable behavior, Java Tutorial.
Common Mistakes
Overloading changes the parameter list and is a different operation from overriding. Static methods do not dispatch like overridden instance methods. A subclass must preserve expectations established by the parent contract.
Preserve the parent contract
An override must keep the behavior callers of the parent type are entitled to use. Narrowing an accepted input range or introducing an unrelated failure can break a caller that never names the subtype. Choose inheritance for substitutable behavior, not merely for reusing a few implementation lines.
Instance-method dispatch uses the actual object type after overload selection has chosen a method signature. Static methods and fields do not follow that same dispatch rule. Inspect the declared receiver and argument types before explaining an overloaded call trace.
A base constructor that invokes an overridable method can call subtype code before subtype initialization is complete. Keep construction-time invariant setup away from that virtual dispatch. Composition often makes resource ownership and independent policy changes easier to represent than a deep inheritance chain.
