这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战
里氏替换原则
定义
1.通俗来说,子类可以扩展父类的功能,但是不能改变父类原有的功能。
2.在程序中将一个父类对象替换成子类对象,程序将不会产生任何错误和异常,反过来不成立。
3.它是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,程序中尽量使用父类类型来定义对象,运行时再确立子类类型,用子类对象替换父类对象。
里氏替换原则包含的含义
1.子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
2.子类可以增加自己特有的方法。
3.当子类覆盖或实现父类方法时,方法前置条件(形参)要比父类的方法更加宽松。 4、当子类的方法实现父类的(抽象)方法时,方法的后置条件(即方法的返回值)要比父类更严格。
继承的作用
1.提高代码重用性
2.多态的前提
方法重写
返回值类型、方法名、参数列表相同构成方法重写
方法重写的两个限制
1.子类重写父类方法时,子类方法的访问修饰符不能比父类更严格。 例子
public class TestExtends {
public static void main(String[] args) {
Father f=new Father();
f.method1();
}
}
class Father{
public void method1(){
}
}
class Son extends Father{
}
此时我们将父类对象用子类对象来替换,是可以的
public class TestExtends {
public static void main(String[] args) {
Father f=new Father();
f.method1();
}
}
class Father{
public void method1(){
}
}
class Son extends Father{
}
重写父类的method1方法,发现报错
原因如下
public static void main(String[] args) {
Father f=new Son();
f.method1();
}
原先我们调用父类的method1()方法是可以调用的,假如我们在用子类对象替换掉父类对象的时候,子类对象的访问权限为private比父类小,那么无法调用也就无法完成替换,违反了里氏替换原则,重写方法时也就不允许我们这样做。
2.子类重写父类的方法时,子类方法不能抛出比父类更多的异常。
public class TestExtends {
public static void main(String[] args) {
Father f=new Son();
try {
f.method1();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Father{
public void method1() throws IOException{
}
}
class Son extends Father{
public void method1() {
}
}
程序中,父类的method1方法抛出了IO异常,那么我们在调用的时候try catch ,子类方法如果没有抛出异常,子类对象可以直接替换父类对象。
但是如果父类的method1方法没有抛出异常,而子类方法在重写时抛出了异常,可以看到程序报错
这是由于我们原来在调用父类方法时是不需要try catch操作的,但是在替换成了子类对象之后却需要try catch,此时引用父类的地方无法透明的使用其子类的对象。所以不允许我们这样重写方法。
两个类不能发生继承关系的依据是什么
a.有没有"is a"关系
b.在两个类有了"is a"关系之后,还要考虑子类对象在替换了父类对象之后,业务逻辑是否变化,如果变化,则不能发生继承关系。
正方形和长方形有"is a"关系。
需要考虑业务场景,在特定的业务场景下,正方形替换长方形,可能会导致业务逻辑发生变化。
public class LiskovSubstitutionPrincipleTest {
public static void main(String[] args) {
Rectangle r=new Rectangle();
r.setLength(20);
r.setWidth(15);
System.out.println("Length:"+r.getLength()+","+"Width:"+r.getWidth());
Utils.change(r);
System.out.println("Length:"+r.getLength()+","+"Width:"+r.getWidth());
Rectangle square=new Square();
square.setLength(12);
System.out.println("Length:"+square.getLength()+","+"Width:"+square.getWidth());
Utils.change(square);
System.out.println("Length:"+square.getLength()+","+"Width:"+square.getWidth());
}
}
class Rectangle{
public double length;
public double width;
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
}
class Square extends Rectangle{
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
this.width=length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
this.length=width;
}
}
class Utils{
public static void change(Rectangle r) {
while(r.getWidth()<=r.getLength()) {
r.setWidth(r.getWidth()+1);
System.out.println("Length:"+r.getLength()+","+"Width:"+r.getWidth());
}
}
}
逻辑分析
这里我们需要实现的一个业务场景是,增加长方形的宽直到宽比长多1,但是正方形长等于宽的特性让我们无法实现这个业务场景,也就是说子类对象在替换父类对象之后,改变了原来的业务逻辑。所以正方形在这种场景下无法继承长方形成为它的子类。