如果对每一个类型为T1的对象o1, 都有类型为T2的对象o2, 使得以T1定义的所有程序P在所有的对象o1都替换成o2时, 程序P的行为都没有发生变化, 那么类型T2是类型T1的子类型. (简单来说就是父类能正常运行的地方, 这个父类的任意子类都能正常运行,且程序逻辑保持不变)
引申意义:
- 子类可以扩展父类的功能,但不能改变原有的功能
含义1: 子类可以实现父类的抽象, 但不能覆盖父类的非抽象(重写).
含义2: 子类可以增加自己特有的方法
含义3: 当子类方法重载父类方法时, 方法的前置条件(入参)要比父类方法的更宽松
含义4: 子类实现父类方法时(重写/重载或实现抽象方法), 方法的后置条件(返回值)要比父类更严格或相等(父类返回Map,子类返回Map或Map的实现类)
不遵循替换原则的代码实例:
正方形与长方形(正方形是一种特殊的长方形)
长方形
public class Rectangle1{
private long length;
private long width;
public Rectangle1() {
}
public Rectangle1(long length, long width) {
if(width > length){
throw new IllegalArgumentException("宽度不能大于长度!");
}
this.length = length;
this.width = width;
}
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;
}
}
正方形继承长方形, 并重写get set方法
public class Square1 extends Rectangle1 {
private long sideLength;
public Square1(long sideLength) {
this.sideLength = sideLength;
}
@Override
public long getLength() {
return sideLength;
}
@Override
public void setLength(long length) {
this.sideLength = length;
}
@Override
public long getWidth() {
return sideLength;
}
@Override
public void setWidth(long width) {
this.sideLength = width;
}
}
定义一个方法,传入一个长方体,将这个长方体的长度调整为宽度的2倍
public class Test1 {
/**
* 调整长度为宽度的2倍
* @param rectangle
*/
public static void resize(Rectangle1 rectangle){
rectangle.setLength(rectangle.getWidth() * 2);
System.out.println("resize方法结束 width:"+ rectangle.getWidth() + " length:"+ rectangle.getLength());
}
public static void main(String[] args) {
Rectangle1 rectangle = new Rectangle1(10, 8);
resize(rectangle);
Rectangle1 square1 = new Square1(10);
resize(square1);
}
}
执行结果为
resize方法结束 width:8 length:16
resize方法结束 width:20 length:20
很显然, 在resize这个方法中 正方体作为长方体的子类并没有得到我们预期的结果, 违背了替换原则, 在这个需求中, 正方体是不能作为长方体的子类的, resize 这个函数并不能接收一个正方体的函数
重新设计正方体与长方体的关系
抽象出一个四边形
public interface Quadrangle {
long getWidth();
long getLength();
}
长方体与正方体分别实现接口
public class Rectangle2 implements Quadrangle {
private long length;
private long width;
public Rectangle2(long length, long width) {
if(width > length){
throw new IllegalArgumentException("宽度不能大于长度!");
}
this.length = length;
this.width = width;
}
@Override
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
@Override
public long getWidth() {
return width;
}
public void setWidth(long width) {
this.width = width;
}
}
public class Square2 implements Quadrangle {
private long sideLength;
public Square2(long sideLength) {
this.sideLength = sideLength;
}
public long getSideLength() {
return sideLength;
}
public void setSideLength(long sideLength) {
this.sideLength = sideLength;
}
@Override
public long getWidth() {
return sideLength;
}
@Override
public long getLength() {
return sideLength;
}
}
测试
public class Test2 {
/**
* 调整长度为宽度的2倍
* @param rectangle
*/
public static void resize(Rectangle2 rectangle){
rectangle.setLength(rectangle.getWidth() * 2);
System.out.println("resize方法结束 width:"+ rectangle.getWidth() + " length:"+ rectangle.getLength());
}
public static void main(String[] args) {
Rectangle2 rectangle = new Rectangle2(10, 8);
resize(rectangle);
Square2 square = new Square2(10);
// 编译报错
// resize(square);
}
}
在编译阶段就避免错误的传入一个正方形进入resize方法
优点
- 约束继承泛滥, 开闭原则的一种体现
- 提高程序健壮性, 降低需求变更时可能引入的风险.
网友评论