1. Basic introduction
- The Liskov Substitution Principle was coined in 1988 by Ms. Lee at M.I.T.
- If for every object o1 of type T1 there is an object O2 of type T2, such that the behavior of program P defined by T1 does not change when all objects o1 are substituted for O2, then type T2 is a subtype of type T1. In other words: all references to a base class must be able to transparently use objects from its subclasses
- When using inheritance, follow the Richter’s substitution principle and try not to override methods of the parent class in subclasses
– Continuing actually makes the two classes more coupled, and can be solved by aggregation, composition, and dependency where appropriate
2. Application cases
-
A method that overrides a parent class by inheritance
public class LiskovSubstitution { public static void main(String[] args) { A a = new A(); System.out.println("11-3 =" + a.func1(11.3)); System.out.println("1-8 =" + a.func1(1.8)); System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); B b = new B(); System.out.println("11-3 =" + b.func1(11.3));// The original intention here is to solve for 11-3 System.out.println("1-8 =" + b.func1(1.8));/ / 1-8 System.out.println("11 + 3 + 9 =" + b.func2(11.3)); }}/ / A class class A { // Return the difference between two numbers public int func1(int num1, int num2) { returnnum1 - num2; }}// Class B inherits class A // Added a new function: add two numbers and then add them to 9 class B extends A { // Here, the class A method is overridden, probably unconsciously public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; }}Copy the code
Running results:
11-3=8 1-8= -7 ----------------------- 11-3=14 1-8=9 11+3+9=23 Copy the code
As can be seen from the operation results, B inherits A and overwrites the func1 method of its parent class (A), resulting in all the codes running the subtraction function calling the method overwritten by class B, resulting in the error of the function that originally operated normally. In actual programming, we often rewrite the method of the parent class to complete the new function, although it is simple to write, but the reusability of the whole inheritance system will be poor, especially when the use of polymorphism is more frequent, the probability of program running error is very large. If you have to rewrite the method of the parent class, the more common approach is: the original parent class and child class inherit a more popular base class, the original inheritance relationship removed, use dependency, aggregation, composition, etc.
In plain English, the Richter substitution principle says that a subclass can extend the functionality of its parent class, but cannot change the functionality of its parent class.
-
Improved code
public class LiskovSubstitution { public static void main(String[] args) { A a = new A(); System.out.println("11-3 =" + a.func1(11.3)); System.out.println("1-8 =" + a.func1(1.8)); System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); B b = new B(); // Because class B no longer inherits from class A, func1 is no longer subtracted by the caller // The finished function will be clear System.out.println("11 + 3 =" + b.func1(11.3));// We want to find 11+3 System.out.println("1 + 8 =" + b.func1(1.8));/ / 1 + 8 System.out.println("11 + 3 + 9 =" + b.func2(11.3)); // Class A related methods can still be used with combinations System.out.println("11-3 =" + b.func3(11.3));// The original intention here is to solve for 11-3}}// Create a more basic base class class Base { // Write more basic methods and members to the Base class } / / A class class A extends Base { // Return the difference between two numbers public int func1(int num1, int num2) { returnnum1 - num2; }}// Class B inherits class A // Added a new function: add two numbers and then add them to 9 class B extends Base { private A a = new A(); // Override class A methods public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; } // We still want to use A's method public int func3(int a, int b) { return this.a.func1(a, b); }}Copy the code
After modification, we use the same method as its parent (A) through class B, so that there are no errors
3. Notes and details
- A subclass can implement an abstract method of the parent class, but cannot override a nonabstract method of the parent class
- Subclasses can add their own special methods
- When a subclass’s method overrides a parent class’s method, the method’s preconditions (that is, the method’s parameters) are looser than the parent method’s input parameters
- When a subclass’s method implements an abstract method of the parent class, the method’s postcondition (that is, the method’s return value) is stricter than the parent class’s