定义:如果对每一个类型T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
定义扩展:一个软件实体如果适用一个父类的话,那一定适用于其自类,所有引用父类的地方必须能透明地使用其自类的对象,子类对象能够替换父类对象,而程序逻辑不变。
引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。
含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
含义2: 子类可以增加自己特有的方法。
含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类的方法的输入参数更宽松。
含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输入/输出值)要比父类更严格或相等。
优点1:约束继承泛滥,开闭原则的一种体现。
优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性提高程序的维护性、扩展性。降低需求变更时引入的风险。
public class Reactangle {
private long length;//长度
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public long getWidth() {
return width;
}
public void setWidth(long width) {
this.width = width;
}
private long width;//宽度
}
public class Square extends Reactangle {
//正方形的边长
private long sideLength;
//getLength() =getWidth() = getSideLength()
public long getSideLength() {
return sideLength;
}
public void setSideLength(long sideLength) {
this.sideLength = sideLength;
}
//getLength() =getWidth() = getSideLength()
@Override
public long getLength() {
return getSideLength();
}
//重写了 长方形设置宽的方法 正方形是长方形的特殊形式 边和长都是一样的
//setWidth() = setLength() = setSideLength()
@Override
public void setLength(long length) {
setSideLength(length);
}
//getLength() =getWidth() = getSideLength()
@Override
public long getWidth() {
return getSideLength();
}
//重写了 长方形设置宽的方法 正方形是长方形的特殊形式 边和长都是一样的
//setWidth() = setLength() = setSideLength()
@Override
public void setWidth(long width) {
setSideLength(width);
}
}
public class Test {
//这是长方形的一个resize方法 判断宽度如果小于等于长度就增加1
public static void resize(Reactangle rectangle){
while(rectangle.getWidth() <= rectangle.getLength()){
rectangle.setWidth(rectangle.getWidth() +1);
System.out.println("Width:" +rectangle.getWidth() +"length" +rectangle.getLength());
}
System.out.println("resize方法结束:" +rectangle.getWidth() +"length" +rectangle.getLength());
}
public static void main(String[] args) {
Reactangle rectangle =new Reactangle();
rectangle.setWidth(10);
rectangle.setLength(20);
resize(rectangle);
}
}
当我们 要用 Square 扩展 这个 Reactangle 类 期望达到的是 只要重写父类的方法 按照一定的标准 适用Reactangle 这个类的方法一定也适用于Square这个类 但是 resize()这方法并不能适用于 Square 因为 resize在判断长度的时候 会比较 边长 和宽度 大小 但正方形边长都一样 这样resize 会陷入while死循环。
public static void main(String[] args) {
Square sq =new Square(10);
resize(sq);
}
上面可以看出 Square 不适合 里式替换 原则 那怎么样 设计 才能进行 约束防止继承的泛滥呢。
public interface Quadrangle {
long getWidth();
long getLength();
}
public class Reactangle implements Quadrangle {
private long length;
public void setLength(long length) {
this.length = length;
}
public void setWidth(long width) {
this.width = width;
}
private long width;
@Override
public long getWidth() {
return width;
}
@Override
public long getLength() {
return length;
}
}
public class Square implements Quadrangle {
public void setSideLength(long sideLength) {
this.sideLength = sideLength;
}
private long sideLength;
public Square(int sideLength) {
this.sideLength = sideLength;
}
@Override
public long getWidth() {
return sideLength;
}
@Override
public long getLength() {
return sideLength;
}
}
//这里传入的是四边形 类型
public static void resize(Quadrangle rectangle){
//下面会报错 因为 四边形方法里没有setWidth()方法 这样就很好的约束了 子类 的行为。 所以上面只能传入(Reactangle rectangle)
while(rectangle.getWidth() <= rectangle.getLength()){
rectangle.setWidth(rectangle.getWidth() +1);
System.out.println("Width:" +rectangle.getWidth() +"length" +rectangle.getLength());
}
System.out.println("resize方法结束:" +rectangle.getWidth() +"length" +rectangle.getLength());
}
里氏替换原则 强调子类的行为 应与父类保持一致 可以达到 子类 替换 掉父类时 功能保持 程序P的功能 能保持不变。
方法入参/返回
public class Base {
//当父类写成Map 或者HashMap 传参 都会调用子类 重写方法
public void method(HashMap map){
System.out.println("父类被执行");
}
}
public class Child extends Base {
@Override
public void method(HashMap map) {
System.out.println("子类HashMao 入参方法被执行");
}
//子类这个方法不能 比父类的 范围大 Map 比 HashMap 范围大
//所以这个方法 是不会被执行的
public void method(Map map) {
System.out.println("子类被执行");
}
}
public class Test {
public static void main(String[] args) {
Base a = new Child();
a.method(new HashMap());
}
}
同理 子类的返回 值比 父类的返回值 范围更大 就会直接报错。