Abstract
Quote
Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
- Not all “Is-a” Relationship should be modelled with Inheritance
What is the correctness of the program?
If a program works correctly using objects of a certain type, it should also work correctly if you replace those objects with objects of a subtype (a class that inherits from the original type).
The Liskov Substitution Principle is about ensuring that subclasses behave in a way that’s compatible with their parent classes. This helps prevent unexpected bugs and makes your code more reliable. Remember, with the power of inheritance comes the responsibility to use it wisely.
Fly example
Consider a base class
Birdwith a methodfly(). If you derive a classPenguinfromBird, butPenguincannot fly, it would violate the LSP because substituting aBirdwith aPenguinwould break thefly()method’s expected behaviour.The derived class should enhance or maintain the behavior of the base class, but never contradict or weaken it.
Length example
In the Liskov Substitution Principle (LSP), if we have a
Squareclass that inherits from aRectangleclass, issues arise when performing certain operations. For example, when we perform thechange widthoperation on both objects:
- The
Squarewill change both its width and length, as they are always equal.- The
Rectanglewill only change its width, leaving its length unchanged.Later, when we perform the
obtain lengthoperation:
- The
Squarewill return its changed length (which is now equal to its changed width).- The
Rectanglewill return its original, unchanged length.This discrepancy in behavior violates the principle of OOP Compatibility, a key aspect of the LSP. The LSP states that objects of a derived class should be substitutable for objects of their base class without affecting the correctness of the program. In this case, substituting a
Squarefor aRectangleleads to unexpected results, breaking the program’s correctness.
How can we fix this?
Leverage a Common OOP Interface: Abstract the
SquareandRectangleproperties into an interface. Implement thewidthandlengthin each respective class. This prevents object substitution when obtaining thelength(which is specific toRectangleobjects), thereby ensuring OOP compatibility.Prevent Inheritance and Method Overriding: Declare the OOP class or individual OOP methods as final. This enhances the ability to reason about program correctness. Noteworthy examples include
java.lang.Mathandjava.lang.String, which are not inheritable.Unit Testing: Employ assertions to verify the LSP during compilation. You can find an illustrative example here.
