I’m sure many of you were as confused as I was when I first read the name of this principle. The reason, in fact, is that this principle was first proposed in 1988 by a woman named Barbara Liskov at the Massachusetts Institute of Technology.

Definition 1: If for every object o1 of type T1, there is object O2 of type T2, such that the behavior of all programs P defined by T1 does not change when all objects o1 are substituted for O2, then type T2 is a subtype of type T1.

Definition 2: All references to a base class must be able to transparently use objects of its subclasses.

Problem: There is A function P1, which is done by class A. Function P1 needs to be extended, and the expanded function is P, where P consists of the original function P1 and the new function P2. If new function P is completed by subclass B of class A, subclass B may fail original function P1 while completing new function P2.

Solution: When using inheritance, follow the Richter substitution principle. When class B inherits from class A, do not overwrite the methods of the parent class A or override the methods of the parent class A, except adding new methods to complete the new function P2.

Inheritance contains a layer of meaning: those who have achieved good method in the parent class (relative to the abstract method), are actually set a series of specifications and contract, although it is not compulsory for all the subclasses must conform to the contract, but if the subclasses for any modification, abstract methods of these products will damage to the entire inheritance system. Richter’s substitution principle is the expression of this meaning.

Inheritance, as one of the three characteristics of object orientation, brings great convenience to program design as well as disadvantages. Using inheritance will bring program invasive, for example, application of portability is reduced, increase the coupling between the objects, if a class is inherited by other classes, is when this class needs to be modified, must consider all the subclasses, and modified the parent class, all involves the function of the subclass may lead to failure.

To illustrate the risks of inheritance, we need to implement A two-digit subtraction function, which is handled by class A.

1 class A{ 2 public int func1(int a, int b){ 3 return a-b; 4 } 5 } 6 7 public class Client{ 8 public static void main(String[] args){ 9 A a = new A(); 10 System.out.println("100-50="+a.func1(100, 50)); 11 System.out.println("100-80="+a.func1(100, 80)); 13 12}}Copy the code

Running results:

100-50 = 50 = 20, 100-80

Later, we need to add a new feature: add the two numbers and then add them to 100, handled by class B. Class B needs to do two things:

  • Subtract two numbers.
  • Add the two numbers and add 100.

Since class A already implements the first function, class B only needs to complete the second function after inheriting from class A. The code is as follows:

1 class B extends A{ 2 public int func1(int a, int b){ 3 return a+b; 4 } 5 6 public int func2(int a, int b){ 7 return func1(a,b)+100; 8 } 9 } 10 11 public class Client{ 12 public static void main(String[] args){ 13 B b = new B(); 14 System.out.println("100-50="+b.func1(100, 50)); 15 System.out.println("100-80="+b.func1(100, 80)); 16 System.out.println("100+20+100="+b.func2(100, 20)); 18 17}}Copy the code

After class B is completed, the running results are as follows:

100-50 = 150, 100-80 = 180, 100 + 20 + 100 = 220

We found an error in the subtraction function that worked normally. The reason is that class B inadvertently overwrites the method of the parent class when naming the method, causing all the codes running the subtraction function to call the method overwritten by class B, resulting in the error of the function that used to run normally. In this case, an exception occurs after subclass B replaces the function done by referring to base class A. 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. It has the following four meanings:

  • 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.

This seems surprising, because we often find ourselves violating The Richter’s substitution principle in our programming, and the program runs just fine. So the question is, what happens if I don’t follow Richter’s substitution rule?

The consequence: your code will be much more likely to fail.