If a program uses fuctionality from a base class, then substituting the base class to a subclass should not change this functionality. In other words, the client (caller) should not be aware of implementation hidden by this base class reference, whether it comes from base class or other derived classes.
This principle defines the rule of subclassing. Inheritance should not by only driven by data reuse, but a subclass must have the same behaviour as base class but implemented in some other way.
Suppose we have two types of bank account: current account and savings account. We could model this by base class
CurrentAccount. Suppose both of the accounts can be closed by
boolean closeAccount(). The base class precondition to close the accont is that balance on this account is not negative, whereas for the savings account in order to close it there are two preconditions – not negative balance and account cannot be closed until minimum savings period passes. So for non-negative balance, the
CurrentAccount account = [ impl ]
boolean result = account.closeAccount();
could be different depending on the implementation, e.g. positive for
CurrentAccount (fulfilled precodition) and negative for
SavingsAccount (if second precodition fails – when not enough time passed to close the account). If the caller remembers the behavior of the base class, then the substitution to subclass would lead to unexpected results. This is because, the subclass
SavingsAccout has more preconditions that base class
The rule of the thumb is that subclass shouldn’t have more strict preconditions than the base class.
The solution to this problem is break existing inheritance, define a separate interface and both of the specific implementations would directly implement that interface. In our case, we have interface
boolean closeAccount(). Both current and savings account would directly implement the interface (
SavingsAccount would not be
Another classic example could be the case of base class
Rectange and subclass
Rectange has setters for width and height and a method
setHeight() implementation has a pre-requisite that it also sets width to the same value as heigth (and the same for
Then, if we call
rectangle.area() then the result would be 2×5 if
rectange is of type
rectange is of type
Square, the result is 2×2 (or 5×5). This leads to confusion.
When extending base class pay attention so that you leave subclass instance in an consistent state. The problems can be that the caller of polymorphic behavior may use overrided and non-overrided methods that change/use the object state. Pay attention that this state is changed constitently. Problems may appear e.g. when a non-overrided method changes the state and the overrided method introduces new state that does not relate to the state set/used by base class method.