软件构造.Construction for Reuse(Variance(型变), Polymorphism(多态),通配符)

形变的内容考试不考

1.Three Types of Polymorphism (多态)

在之前记过多态:
https://blog.csdn.net/qq_41359808/article/details/89004124
其实就是三种:1.方法重载,2.泛型编程,3.一个变量名可以代表多个类的实例(eg.List,ArrayList,LinkedList之间的关系)

2.Behavioral subtyping and Liskov Substitution Principle (LSP)(Liskov替代原则,Liskov是一个人名。。。)

2.1Java中编译器强制执行的规则:

  • 子类型可以添加,但不能删除方法
  • 具体类必须实现所有未定义的方法
  • 重写方法必须返回相同的类型或子类型
  • 重写方法必须接受相同的参数类型
  • 覆盖方法可能不会引发其他异常

也适用于指定的行为:(下面这三行的意思就是:子类型对于输入的要求应该更宽松,但是输出应该更严格。。。要求的更少,但是给的更多。。。!!!)

  • 相同或更强的不变量
  • 所有方法的相同或更强的后置条件
  • 所有方法的相同或较弱的前提条件

3.Variance(型变)

Variance:Variance refers to how subtyping between more complex types relates to subtyping between their components. 型变是指当子类型关系出现在更加复杂类型中时,如何处理新类型中的子类型关系。
eg
类a是类b的父类,
类c是类d的父类
c和d中分别有void function(a value),void function(b value)函数(overload,重载)
这种情况下就构成了型变的问题。我举的例子是Covariant(协变)的情况
variance名字叫型变但是实际上并没有什么发生变化!!!只不过是表示了一种关系而已!!!

3.1Three types of variance
在这里插入图片描述
下面的第三个Invariant的意思因该是没关系。而不是不变。。。

在这里插入图片描述
下面的图中的箭头指向父类!!!
在这里插入图片描述

下图是型变在重载当中的情况overloading

最左边的图:参数列表和返回值具有的从属关系
在这里插入图片描述
下图中的型变是安全的。
因为子类CatShelter中的参数列表要求比父类AnimalShelter的参数列表要求更加宽泛。
在这里插入图片描述

4.Variance in generics

Generics are type invariant in Java Java中的泛型是不“型变”的
在这里插入图片描述
Generic type info is erased in running-time(i.e. compile-time only)!!!泛型信息只存在与编译阶段,在运行阶段会被擦除!!!
Cannot use instanceof to check generic type,不管是什么时候instanceof尽量少用

在这里插入图片描述
在这里插入图片描述

5.Type Erasure

泛型信息只存在于编译阶段!!!在运行时会被擦除!!!
在这里插入图片描述
eg
在这里插入图片描述

下图中当泛型被声明为某个类的子类的时候!!!在擦除之后原来T的位置都变成了父类的类型!!!

在这里插入图片描述

下图中的两个都是错误的,因为泛型的信息在运行的时候已经被擦除掉了。。。

在这里插入图片描述
下图中的在这里插入图片描述
显然由于List是个接口由ArrayList实现。故子类型成立
但需要注意的是
在这里插入图片描述

上面这一行是不成立的!!!因为涉及到了泛型

下面的代码中的第二行编译器不会报错,但是存在安全隐患

在这里插入图片描述
下面的图!!!,因为Pair中涉及到了泛型,所以两个Pair之间没有关系
在这里插入图片描述

6.通配符

  1. 在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义
  2. <? extends Object>代表上边界限定通配符
  3. 上边界通配符直接使用add()方法受限,但是可以用来获取各种数据类型的数据,并赋值给父类型的引用。
  4. <? super Object>代表下边界限定通配符。
  5. 下边界通配符使用get()方法受限(不使用强转),但是可以添加特定类型的值,用于将对象写入到一个数据结构里

eg

package tongpeifu;

public class animal {

}
package tongpeifu;

public class cat extends animal{

}
package tongpeifu;

public class whitecat extends cat{

}
package tongpeifu;

import java.util.*;

public class Main {
	public void main(String[] Zing) {
		List<cat> objs = new ArrayList<>();
		objs.add(new cat());
		objs.add(new whitecat());
		objs.add(new animal());//error

	}

}

在不使用通配符的情况下一切都很正常,
可以添加cat
也可以添加cat的子类whitecat,因为whitecat中具有cat的方法和字段
但是不可以添加animal,因为animal是cat的父类,cat中有的字段或者方法在animal中不一定具有
在这里插入图片描述
编译器报错如下
在这里插入图片描述
在使用上边界通配符的时候<? extends Object>

package tongpeifu;

import java.util.*;

public class Main {
	public void main(String[] Zing) {
		List<? extends animal> animals = new ArrayList<>();
		animals.add(new cat()); //compile error
		animals.add(new animal()); //compile error
		animals.add(new Object()); //compile error
		animals.add(new whitecat()); //compile error
		animals.add(null); //succeed, but it is meaningless.
		animal animal = animals.get(0); //succeed!

	}

}

因为上边界通配符(extends)不支持add方法,所以add什么都是没有用的,但是上边界通配符支持get方法
类似get的返回T的方法可以使用的原因:编译器可以知道添加的对象至少得支持animal中的字段和方法。所以返回一个animal类型是可以做到的(类型强制转换)。,也就是说T get(int pos))是安全的
类似add(E e)的方法则不安全,类型擦除机制会导致在运行的时候e一律被替换成animal,但是e可能是animal的子类,所以e中的方法和字段可能会损失一部分
在这里插入图片描述

package tongpeifu;
import java.util.*;
public class Main1 {
	public void main(String[] Zing) {
		List<? super cat> animals = new ArrayList<animal>();
		animals.add(new cat());
		animals.add(new whitecat ());
		animals.add(new animal()); //compile error
		animals.add(new Object()); //compile error
		Animal animal = animals.get(0); //compile error
		Object o = animals.get(1); //succeed, but it is meaningless. 
	}

}

下边界通配符(super)有add方法,get方法受限
对于List中的 T get(int pos)方法,当指定类型是“? super Cat”时, get方法的返回
类型就变成了“? super Cat”, 即返回类型可能是Cat或者Cat的基类型,compiler无法确定具体类型,因此拒绝调用任何返回类型为T的方法(除非是读取为Object类)
但调用类似add(E e)的方法则安全, 传入Cat及其子类(WhiteCat)是安全的, 因为compiler知道这个List包含的是Cat或Cat的基类对象。
因此,Java此时禁止了List中所有具有泛型返回类型的方法,如:get()
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值