Disclaimer: The content of this article is from the network of books collated, not original.
The second definition is the clearest, the popular point is that as long as the parent class can appear where my subclass can appear, and the calling subclass does not produce any errors or exceptions, the caller may not need to know whether it is a parent class or a subclass at all. But the reverse is not possible, there are sub-categories where the occurrence of the parent class may not be able to adapt.
subclasses must fully implement the methods of the parent class. when we do system design, we often define an interface or abstract class, and then write the implementation, call the class directly into the interface or abstract class, in fact, here has used the Richter replacement law. Example of a CS shooting:
The main function of the gun is to shoot, how to shoot is defined in each specific subclass, pistol is a single range relatively close, rifle power big shot Chengyuan, machine guns used to fire, and then defined in the Soldier Class A method Killenemy kill the person, the use of gun, specifically use what gun to the enemy, when the call to know , I first look at the Abstractgun class program:
publicabstractclass//枪用来干什么的?射击杀戮! publicabstractvoidshoot
The following are the implementation classes for three specific firearms:
Public class handgun extends abstractgun { //Pistol features are easy to carry, short range @Override Public void Shoot() {System.out.println ("Pistol shot ..."); } } Public class Rifle extends abstractgun{ //Rifle is characterized by long range, great power Public void Shoot() {System.out.println ("Rifle shooter ..."); } } Public class machinegun extends abstractgun{ Public void Shoot() {System.out.println ("Machine gun fire ..."); } }
Then look at the soldier class source code:
publicclass Soldier { publicvoidkillEnemy(AbstractGun gun){ System.out.println("士兵开始杀人..."); gun.shoot();
Notice here the construction method, we ask to come in is an abstract gun, specifically pistol or rifle need to be passed in when called, we look at the Client class:
publicclasspublicstaticvoidmain(String[] args) { //产生三毛这个士兵 new Soldier(); sanMao.killEnemy(new Rifle()); } }
Operation Result:
士兵开始杀人... 步枪射击...
In this program, we give Sanmao this soldier a rifle, and then began to kill, if Sanmao to use machine gun of course, can also, directly to the Sanmao.killenemy (new Rifle ()) modified to Sanmao.killenemy (new machinegun ( ), soldier doesn't have to know which subclass it is. It is important that we call other classes in the class to use the parent class or interface, and if the parent class or interface cannot be used, then the design of the class has violated the LSP principle.
Let's think about it, if we have a toy pistol, how do we define it? Let's first add a class to the class diagram:
Added a Toygun class that inherits from the Abstractgun abstract class. First of all, we think that the toy gun can not be used to shoot, kill the dead, of course, you have to put the toy gun to the head can also hit the dead, this is not in the shoot method of the function. Let's look at the Toygun class:
publicclass ToyGun extends AbstractGun {//玩具枪式不能射击的,但是编译器又要求实现这个方法,怎么办?虚假一个呗! @Overridepublicvoidshoot() { //玩具枪不能射击,这个方法就不能实现了 } }
Then we look at this scenario:
publicclass Client {publicstaticvoidmain(String[] args) { //产生三毛这个士兵 new Soldier(); sanMao.killEnemy(new ToyGun()); } }
Soldiers use toy guns to kill, see the results of the operation:
士兵开始杀人...
Broken, the soldier took the toy gun to kill, cannot shoot the bullet! If there is such a thing in CS game, then you wait for the person to explode the head, and then look at their bleak fall. In this case, we have found that the business call class already has the problem, this is the business has not been able to run, then what? There are two ways to solve this problem:
- In the soldier to increase the judgment of instanceof, if it is a toy gun, you do not have to kill people. This method can solve the problem, but you need to know that in the project, especially the product, to add a class, I want to have all the classes that have to do with this parent class need to be modified, do you feel feasible? If you have this problem in your product, because fixing such a bug requires all classes that have a relationship with this parent to add a judgment? Customers do not jump up to do with you! Do you still want your clients to be loyal to you? This scheme negates.
- Toygun out of inheritance, to establish a separate parent class, in order to do the code can be taken, can be associated with Abastractgun to establish a relationship with the delegation, such as:
For example, you can define the sound in the Abstracttoy, the shape is entrusted to the Abstractgun, the simulation gun is of course to let the shape, sound and real gun are the same, and then the two parents under the sub-class development, and do not affect each other.
In Java's basic knowledge, each teacher will speak of inheritance, Java three major features, inheritance, encapsulation, polymorphism, inheritance is to tell you to have the parent class methods and properties, and then you can override the parent class method. According to the principle of inheritance of the class, we have no problem with the toy gun Abstractgun, after all, it is also a gun, but to our specific project to consider this question: whether the sub-class is complete to achieve the parent class business, otherwise it will appear like the above to shoot the enemy when found to be the toy gun jokes.
subclasses can have their own personalities . Subclasses can of course have their own behavior and appearance, that is, methods and properties, then why do you want to mention it here? is because the Richter scale substitution law can be used in a positive way, but not in reverse. In the presence of subclasses, the parent class may not be competent. Or the example of the firearm just now to illustrate that the rifle has several relatively loud models such as AK47, G3 sniper rifle, etc., we look at the class diagram:
Quite simply, G3 inherits the Rifle class, and the Sniper (snipper) uses the G3 sniper rifle directly, so let's take a look at the program:
publicclass G3 extends Rifle {//狙击枪都是携带一个精准的望远镜 publicvoidzoomOut(){ System.out.println("通过望远镜观看敌人..."publicvoidshoot(){ System.out.println("G3射击..."); } }
Then we'll declare a sniper class:
publicclass Snipper {publicvoidkillEnemy(G3 g3){ //首先看看敌人的情况,别杀死敌人,自己也被人干掉 g3.zoomOut(); //开始射击 g3.shoot(); } }
Sniper, why is it called snipper? Snipe translation came to be a snipe, that is conversely, yuwengdeli in the animal, the British nobility to India hunting, found the snipe is very clever, people flew away, no way to start camouflage, remote precision shooting, so Snipper was born.
Let's take a look at how the business scenario is called:
publicclasspublicstaticvoidmain(String[] args) { //产生三毛这个狙击手 new Snipper(); sanMao.killEnemy(new G3()); } }
The results of the operation are as follows:
通过望远镜观看敌人... G3射击...
Here we directly call the subclass, a sniper is very dependent on firearms, not to mention a model of the gun, is to change a gun of the same model will affect the shooting, so here is directly passed in the sub-class. At this time, can we directly use the parent class to pass in? Modify the Client class:
publicclasspublicstaticvoidmain(String[] args) { //产生三毛这个狙击手 new Snipper(); new Rifle(); sanMao.killEnemy((G3)rifle); } }
Display is not possible, will be reported in the run-time java.lang.ClassCastException anomaly, which is often said to the downward transformation (downcast) is not safe, from the Richter replacement law, there is a subclass of the local parent may not appear.
input parameters can be magnified when overriding or implementing a method of the parent class. the input parameter in the method is called the precondition, what does it mean? We have done Web Service development should know that there is a "contract first" principle, that is, the first definition of the WSDL interface, the development of the two sides of the protocol, and then the respective implementation. The Richter substitution law also requires a contract, the parent class or interface, which is also called the design by contract, and the contract precedence design is fused with the Richter substitution law. The contract is made, but the contract has preconditions and preconditions, and the precondition is that if you want me to execute it, I must satisfy my condition; the post-condition is that I am done and must conform to the stipulated contract. This is more difficult to understand, let's take a look at an example, we first define a father class:
publicclass Father { publicdoSomething(HashMap map){ System.out.println("父类被执行..."); return map.values(); } }
This class is very simple, which is to convert the HashMap to the Collection collection type, and then we look at the subclass:
publicclass Son extends Father {//放大输入参数类型 publicdoSomething(Map map){ System.out.println("子类被执行..."); return map.values(); } }
Attention to look at the subclass of the method, and the same method name of the parent class, but not the method of overriding the parent class, you add a @override try, error, why? is the input parameter type is different, the compiler is not considered to be the method of overriding the parent class, then what is this? is overloaded (overload)! No fuss, not a class can not be overloaded? What does inheritance mean, that subclasses have all the properties and methods of the parent class, and that the duplicate input parameter type of the method name is different, of course, overloaded. Let's look at the business invocation class again:
publicclass Client { publicstaticvoidinvoker(){ //父类存在的地方,子类就应该能够存在 new Father(); new HashMap(); publicstaticvoidmain(String[] args) {
The results of the operation are as follows:
父类被执行...
The Richter substitution rule says that the parent class appears where the subclass can appear, and we change the parent section above to subclass, and the program is as follows:
public Class Client {public static void invoker () {//where the parent class exists, Subclasses should be able to exist son f = new Son (); HashMap map = new HashMap (); F.dosomething (map); } public static void main (string[] args) {invoker ();}}
Run the results still the same, see what's going on? The input parameter of the parent class method is the HashMap type, the input parameter of the subclass is the Map type, that is, the range of the input parameter type of the subclass is enlarged, the subclass is passed to the calling class instead of the parent class, and the method of the subclass is never executed, this is correct, if you want to let the subclass method run, You must override the method of the parent class. You can think of this, in a Invoker class associated with a parent class, called a method of a parent class, subclasses can override this method, you can also overload this method, if you want to expand this precondition, is the type of the input parameter is greater than the type of parent class coverage. may be more difficult to understand, then we think again, if the input parameter type of the Father class is greater than the input parameter type of the subclass, what is the problem? There will be a place where the parent class exists, and the subclass may not exist, because once the handle class is passed in as a parameter, the caller is likely to enter the subclass's method category. Let's change the example above and look at the parent class first:
publicclasspublicdoSomething(Map map){ System.out.println("Map 转Collection被执行"); return map.values(); } }
To modify the parent class's precondition to the Map type, we then modify the input parameters of the class method at once, and reduce the type range of the input parameters relative to the parent class, that is, to reduce the preconditions:
publicclass Son extends Father {//缩小输入参数范围 publicdoSomething(HashMap map){ System.out.println("HashMap转Collection被执行..."); return map.values(); } }
Then look at the business Scenario class:
publicclasspublicstaticvoidinvoker(){ //有父类的地方就有子类 new Father(); new HashMap(); publicstaticvoidmain(String[] args) { invoker(); } }
The results of the operation are as follows:
父类被执行...
So what's the problem with introducing the Richter scale substitution rule? There is a parent class where the subclass can be used, OK, let's modify this client class, the program is as follows:
publicclasspublicstaticvoidinvoker(){ //有父类的地方就有子类 new Son(); new HashMap(); publicstaticvoidmain(String[] args) { invoker(); } }
The results of the operation are as follows:
子类被执行...
Are you finished?! Subclasses in the absence of a method to rewrite the parent class, the subclass method is executed, this will definitely cause future business logic confusion, because in the application of the project, the parent class is generally abstract class, the subclass is the implementation class, you pass a such implementation class will cause a lot of unexpected business logic confusion, so The precondition of a method in a subclass must be the same as or looser than the precondition of the method being overridden in the superclass.
the method of overriding or implementing the parent class is that the output can be scaled down. What does this mean? A method return value for a parent class is a type T, and the same method (overload or override) of the subclass returns a value of S, then the Richter substitution law requires s to be less than or equal to T, which means that either s and T are the same type, or S is the subclass of T, why? In two cases, if it is overridden, the input parameters of the method parent class subclass is the same, two methods of the range value S is less than or equal to T, this is the overriding requirement, this is the most serious, the subclass of the method of overriding the parent class, it is natural; if overloaded, requires different input parameters of the method, In the case of the Richter substitution law, the input parameters of the subclass are greater than or equal to the input parameters of the parent class, which means that the method you are writing is not called, so refer to the preconditions above.
The purpose of the birth of the Richter replacement law is to enhance the robustness of the program, while the version upgrade can also be very good compatibility, add subclasses, the original subclass can continue to run. In our project implementation is that each sub-class corresponds to a different business meaning, using the parent class as parameters, passing different sub-classes to complete different business logic, very perfect!
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Six design principles-the Richter replacement principle "Liskov Substitution Principle"