前景知识
liskov替换原则,Lsp原则————所有使用父类对象的地方都可以透明的使用子类。
1.lsp原则是面对对象语言的设计原则。
-
引申出了的多态
Object s = "123" Number n = 123 我们声明一个父类对象,然后用子类进行实例化 -
子类继承父类的方法,入参更宽松
-
子类继承父类的方法,出参更严格
其中2和3有点绕,也都是基于【所有使用父类对象的地方都可以透明的使用子类】这一核心。
2.协变,逆变,不变 本身是用于描述继承关系的转化。
A是B的子类,经过f(x)类型转换之后,f(A)是f(B)的子类,就认为是协变,反之称之为逆变器,如果二者没关系,是不变。
具体怎么判断A是B的子类,就是使用上面前景知识的多态转化 比如:
Object s = "123" ----> 我们说String是Object的子类
Object[] arr = new String[]; ----> 编译通过,我们认为数组这种转换关系是协变的。
List<Object> list = new ArrayList<String>(); ----> 编译不通过,我们认为list这种转换关系是不变的。
泛型本身是不变的,但是很多时候需要实现逆变、协变,要想达到继承关系,需要使用关键字 extends, super
List<? extends Number> list = new ArrayList<Integer>();//编译通过
List<? extends Number> list2 = new ArrayList<Object>(); //编译报错
list.add(123); //编译报错
Number s1 = list.get(0); //编译通过
Integer s2 = (Integer) list.get(0); //编译通过
//编译通过
List<? super Number> list1 = new ArrayList<Object>(); //编译通过
list1.add(123); //编译通过
list1.add(456.0); //编译通过
Number number = list1.get(0); //编译报错
首先 Integer 是 Number 的子类, Number 是 Object 的子类。 但是现在:
List<Integer> 是 List<? extends Number> 的子类,因此说,extends实现了协变
ArrayList<Object> 是 List<? super Number> 的子类,因此说 super 实现了逆变
3.kotlin in out
先说结论: 二者都是修饰泛型的,因为泛型是不变的,我们想实现协变,逆变,必须像java一样增加关键字
out producer 返回值,实现协变
in consumer 入参,实现逆变
* 通配符,匹配
使用原则:
如果泛型只用于返回值,就用out
如果泛型只用于入参数,就用in
如果泛型同时使用于返回值和入参,用 * 通配符吧。
这个概念理解的前提是:
out实现协变---- 返回值更加严格
in实现逆变 ---- 入参更加宽松