【软件构造】LSP,协变和逆变

1.LSP原则:

内容:

任何基类可以出现的地方,子类一定可以出现。

四条原则:

  • 子类完全拥有父类的方法,当子类不是抽象类时,必须将父类的抽象方法全部实现。
  • 子类可以拥有父类没有的方法
  • 在子类中override或实现父类的方法时,前置条件需要更加宽松(形参比父类方法更加宽松)。
  • 在子类中override或实现父类的方法是,后置条件需要更加严格(返回值)。
个人理解:
  • LSP的四条原则其实是为了保证同一件事:父类是可以被任意一个子类在任何时候替换的。这也是继承和复用的前提。
  • 总的来说,对于后两条的理解,其实就是一定要保证子类的各种输入输出在其父类中一定是合法的。实际上我们在写代码的时候,对于同一个函数,如果前置条件宽松,后置条件严格,spec实际上是增强了的。单从后两条来看,对于子类与父类来说,我们也可以将子类看成是拥有“改进过spec”的更强的方法的父类。

2. 协变和逆变:

内容:

  • 协变:
    • 对于某一个方法Fun(),其传入参数为A、B,当A为B的子类时,Fun(A)也是Fun(B)的子类。
  • 逆变:
    • 对于一个方法Fun(),其传入参数为A、B。当A为B的子类时,Fun(A)也是Fun(B)的子类。

一些奇怪的应用:

1. 类型擦除

例如这段代码:

List<Animal> ani = new ArrayList<>();
List<Goat> goats = new ArrayList<>();

虽然在这里,Goat类是Animal的子类,但是Animal的List和Goat的List并不存在继承关系,因为在编译阶段,Animal和Goat的位置是泛型结构,编译之后会被编译器抹去,运行时,ani和goats两个变量只会保留List这个类型,又因为后边实例化了ArrayList,实际打印类型我们可以得到:

System.out.println(ani.getClass().getName());
//	OUTPUT:
//		java.util.ArrayList
System.out.println(goats.getClass().getName());
//	OUTPUT:
//		java.util.ArrayList

实际上,类型擦除就是将泛型的结构出填写的类型替换为其上限类型(个人理解,就是规定好的能够兼容这个类的“最高父类”),这里没有指定上限类型,Animal和Goat将全部被替换为Object。因此,List和List,在运行时都是List,继承关系消失。

2. 泛型的协变和逆变:

例如这段代码:

public class Animal(){
	String name;
	public void eat(Object a){
	}
}
public class Goat extends Animal(){
	@Override
	public void eat(Plant a){
	}
}

对于:

// 下边的一行是不能通过的。
List<Animal> animals = new ArrayList<Goat>();

这两段代码描述了一个情形:我们设置了泛型结构,但是我们还需要避免类型擦除,实现泛型的协变和逆变。这时,通配符<?>登场了。
这里,我们定义了一个Animal的集合,但是想把它作为一个Goat集合使用,按理说应该是被允许的,但是java(junk)的编译器会报错,因为在java编译器的眼里,这俩玩意不是已经“父子兵”,而是“亲兄弟”了。这时候,我们就需要借助上界通配符<? extends Animal>的帮助 ,用来规定上限类型,如下:

List<? extends Animal> animals2 = new ArrayList<Goat>();
Animal ani = animals2.get(0);

对于:

//  下面一行编译是不能通过的
List<Goat> goats = new ArrayList<Animal>();

这里,我们所需要的和上一个问正好相反,此时下界通配符<? super Goat>登场:

List<? super Goat> animals2 = new ArrayList<Animal>();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值