Generics
https://www.bilibili.com/video/BV1T441117u8
泛型
继承类型
1 | class Button extends TextView |
不能把子类的List对象 ArrayList
Java
? extends TextView
1 | List<TextView> textViews=new ArrayList<>(); |
? super Button
1 | ArrayList<Button> textViews1 = new ArrayList<TextView>(); |
Java kotlin对比
Java
1 | List<? extends TextView> textViews; |
Kotlin
1 | var textVies: List<out TextView> |
kotlin新增用法
表示我这个类型,这个类只用来输出或者输入
1 | interface Producer<out T>{ |
*用法
Java
1 | List<?> textViews; |
Kotlin
1 | var textVies: List<*> |
如果类型声明里,已经有了out或者in,这个限制在变量声明时也依然存在,不会因为*去掉
1 | interface Counter<out T : Number>{ |
类型声明上界
注意这里是T extend Animal ,和带? extend Animal 的声明不是一个东西
java
1 | class Monster<T extends Animal & Food>{ |
kotlin
1 | class Monster<T : Animal>{} // 一个上界 |
refield
java
1 | <T> void printIfTypeMatch(Object item){ |
kotlin
1 | fun <T> void printIfTypeMatch(item : Any){ |
Java泛型类
1 | public class Order<T> { |
子类不是泛型类
父类指明类型,由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型。
1 | public class SubOrder extends Order<Integer> { |
子父类关系
类A是类B的父类,G和G二者不具备父子类关系,也不具任何关系。
类A是类B的父类,A
反证法:
https://www.bilibili.com/video/BV1fi4y1b7NM?p=11
子类是泛型类
1 | public class SubOrder1<T> extends Order<T> { |
泛型方法
1 | public class Person3<T> { |
泛型接口
1 | interface PerInt<T> { |
- 泛型接口的实现类 可以指定具体的泛型接口的具体泛型类型
1 | class PerImtImpl : PerInt<String> { |
泛型接口的实现类,如果没有指定具体的泛型类型,必须要在这个实现类中声明一个泛型类型的占位符给接口用
1
2
3
4class PerImtImpl01<T> : PerInt<T> {
override fun show(name: T) {
}
}
类型擦除
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
泛型的作用是能在编译期间就提示错误,而不是运行时。
1 | val p1 = PerImtImpl01<String>() |
通配符
Java中的继承 ,在泛型中并不是父子类关系
1 | class PerImtImpl01<T> : PerInt<T> { |
1 | val p3 = PerImtImpl01<Number>() |
符号区别
Java用 ? Kotlin 用 *
无界通配符 意味着可以使用任何对象,因此使用它类似于使用原生类型
类A是类B的父类,G和G的公共的父类是: G<?>
遍历List
1 |
|
对于List> 就不能向其添加数据。如果任何类型引用通过赋值给 List> 能添加数据,那定义泛型就没意义了
1 | List<?> list = null; |
上下界
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法
1 | public class StaticGenerator<T> { |
- 上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。
- 下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景。
上界通配符 <? extends T>
泛型中的继承
泛型中的继承不是Java中的继承。也就是说java中的父子类关系, 在泛型中 并不是父子类关系。
1 | class Fruit { |

? extends Fruit赋值 ,无法添加数据,不知道子类到底多小.
? extends Fruit 可以理解为 <= Fruit,
List<? extends Fruit> 可以作为 List
1 | // List<Fruit> list1 = new ArrayList<Apple>(); //编译报错,泛型具体类型 List<Fruit> ,ArrayList<Apple> 不存在继承关系 |
读取数据
? extends Fruit 所以泛型最大的父类就是Fruit,子类型可以直接复制给父类
1 | /** |
你会发现无法往里面设置任何数据,按道理说我们将泛型类型设置为? extend Fruit。按理说我们往里面添加Fruit的子类应该是可以的。但是Java编译器不允许这样操作。<? extends Fruit>会使往盘子里放东西的set()方法失效。但取东西get()方法还有效
原因是:Java编译期只知道容器里面存放的是Fruit和它的子类,具体是什么类型不知道,可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在后面看到Plate< Apple >赋值以后,盘子里面没有标记为“苹果”。只是标记了一个占位符“CAP#1”,来表示捕获一个Fruit或者Fruit的派生类,具体是什么类型不知道。所有调用代码无论往容器里面插入Apple或者Meat或者Fruit编译器都不知道能不能和这个“CAP#1”匹配,所以这些操作都不允许。
一个Plate<? extends Fruit>的引用,可能是一个Plate
类型的盘子,要往这个盘子里放 当然是不被允许的.
但是上界通配符是允许读取操作的。例如代码
这个我们很好理解,由于上界通配符设定容器中只能存放Fruit及其子类,那么获取出来的我们都可以隐式的转为Fruit基类。所以上界描述符Extends适合频繁读取的场景。
Java类型擦除只会擦除到Fruit类型,如果没有指明边界,那么类型参数将被擦除到Object.
写入数据
可以发现任何数据都不能存入。[-∞,Fruit]范围,-∞不知道具体的类型小到多少
1 | list1.add(new Apple()); //编译报错,<= Fruit范围, List<? extends Fruit>实际类型可能比是GreenApple,比Apple范围还小,随意存不了数据 |
下界通配符 <? super T>
? super Fruit赋值,只能添加数据 。
? super Fruit 可以理解为 >= Fruit
1 | //只能修改数据,不能读取数据 |
读取数据
1 | Object object = list6.get(0); //理论上不能读取,但是都到Object还是可以的 |
写入数据
? super Fruit范围是是 [Fruit,+∞), 所以Fruit范围内的都可以添加
1 | list6.add(new Fruit()); |
下界通配符<? super Fruit>不影响往里面存储,但是读取出来的数据只能是Object类型。
下界通配符规定了元素最小的粒度,必须是Fruit或其基类,那么我往里面存储Fruit及其子类都是可以的,因为它都可以隐式的转化为Fruit类型。但是往外读就不好控制了,里面存储的都是Fruit及其基类,无法转型为任何一种类型,只有Object基类才能装下。
https://juejin.im/post/5b614848e51d45355d51f792
www.jianshu.com/p/dd34211f2565
https://www.bilibili.com/video/BV1xJ411n77R?p=7
kotlin
out in 和 extend super没关系
out
只能获取 不能修改
1 | var list:MutableList<out Fruit> = ArrayList<Apple>() // ? extends Fruit 实际类型可能比是Apple小 |
https://www.bilibili.com/video/BV1xv411k7Dd?p=4&spm_id_from=pageDriver
https://noteforme.github.io/2021/08/21/kotlin-object/#Out-In
https://www.bilibili.com/video/BV1Hy4y1H7oS?p=7
泛型数组
可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
1
2
3
4
5
6
7
8
9ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
ArrayList<String> strList = new ArrayList<>();
strList.add("abc");
listArr[0] = strList;
String s = listArr[0].get(0);
System.out.println(s);可以通过java.lang.reflect.Array的newInstance(Class
,int) 创建T[]数组 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class FruitArr<T> {
private T[] array;
public FruitArr(Class<T> clz, int length) {
this.array = (T[]) Array.newInstance(clz, length);
}
public void put(int index, T item) {
array[index] = item;
}
public T get(int index){
return array[index];
}
public T[] getArray(){
return array;
}
}1
2
3
4
5
6FruitArr<String> fruit = new FruitArr<>(String.class,3);
fruit.put(0,"苹果");
fruit.put(1,"西瓜");
fruit.put(2,"香蕉");
String s1 = Arrays.toString(fruit.getArray());
System.out.println(s1);
泛型与反射
1 | Class<Person> personClass = Person.class; |
https://www.bilibili.com/video/BV1xJ411n77R?p=12
$i$f$withM 有时候看到$以为是操作符,其实就是$i$f$withM整体的变量
1 | Array<out Any> 对应于 Java 的 Array<? extends Object> |