1. 里式替换原则
1.1 参考文章
1. 2自己理解记录如下:
1.2.※,当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
class Parent { public Map m1(CharSequence cs) { System.out.println("父类方法被调用"); } } class Child { public HashMap m1(String str) { System.out.println("子类方法被调用") } } class Test { public static void main(String[] args) { Parent p = new Parent(); p.m1("Tonus");//使用String类型参数调用父类,父类方法m1中的参数最接近String,所以被调用 //根据里式替换原则,父类一定要可以被子类无条件替换 Child c = new Child(); c.m1("Tonus");//此时子类相当于有两个重载的方法,因为子类参数类型就是String,所以子类会被调用,和原来的“父类被调用”不一致了!!!这就违反了里式替换原则。
如果将父类和子类的参数类型反过来,子类替换父类后,调用的依然是更具体的String类型的父类,所以和原来一直的,符合里式替换原则。 } }
1.2.※,当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
这个规则不止是里式替换原则中的规则,更是Java本身就规定必须符合的规则。子类返回值类型必须小于等于父类返回值类型(同父类返回值类型相同或是其子类)。如果不符合这条规则编译器会报错。原因是:向上转型在Java中是被允许的,但是向下转型只有被转型的父类本身是子类类型时才被允许,否则运行时报错(编译不报错)。比如 Map map = new HashMap(); //向上转型默认是可以的。但是 HashMap hashMap = new Map(){...};//向下转型默认是不允许的,必须强转(可能会报错)。
继续用上面的代码示例:假如有如下使用场景 class Test { public static void main(String[] args) { Parent p = new Child(); Map map = p.m1("Tonus");//此时p.m1()实际上是HashMap,但是编译时被视为Map类型,这里向上转型在Java中是被允许的,无论编译还是运行都不会报错。
如果父类返回HashMap,子类返回Map,那么就会出现向下转型的情况:p.m1();是个Map类型,但是被编译器视为HashMap类型,就出现了Map向下转型为HashMap的场景,
而且这个Map实际并不是HashMap类型,所以使用强转后在运行时会报错(ClassCastException)! } }
1.2.※,子类方法不能降低父类方法的可见性,特别地,如果父类是public,那么子类也必须是public。
这个规则符合里式替换原则,同时也是Java本身就规定必须符合的规则。如果降低了父类方法可见性,父类可以访问的方法子类不能访问就不符合“父类出现的地方一定要被子类无条件替换”的里式替换规则了。
1.3,