https://www.bilibili.com/video/BV1T441117u8
泛型 继承类型 1 class Button extends TextView
不能把子类的List对象 ArrayList 赋值给父类的List引用 List
Java
? extends TextView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 List <TextView> textViews = new ArrayList <>(); List <TextView> textViews = new ArrayList <Button>(); List <? extends TextView> textViews = new ArrayList <Button>(); TextView textView = new TextView (this ); textViews.add(textView); TextView textView = textViews.get(0 ); List <Button> buttons = new ArrayList <Button>(); textView = buttons;
1 2 3 4 5 6 7 8 9 10 ArrayList <Button> textViews1 = new ArrayList <TextView>();ArrayList <? super Button> textViews2 = new ArrayList <TextView>(); Button button = textViews2.get(0 ); textViews1.add(new Button (this ));
Java kotlin对比 Java
1 2 3 List<? extends TextView > textViews; List<? super Button> textViews;
Kotlin
1 2 var textVies: List<out TextView>var textVies: List<in TextView>
kotlin新增用法 表示我这个类型,这个类只用来输出或者输入
1 2 3 4 5 6 7 interface Producer <out T >{ fun produce () : T }interface Consumer <in T >{ fun consume (product:T ) }
*用法 Java
1 2 3 List<?> textViews; 相当于 List<? extends Object > textViews;
Kotlin
1 2 3 var textVies: List<*> 相当于var textVies: List<out Any>
如果类型声明里,已经有了out或者in,这个限制在变量声明时也依然存在,不会因为*去掉
1 2 3 4 5 6 7 interface Counter <out T : Number >{ fun count () : T }var counter: Counter<*> =; 那么Counter<*>就相当于下面的情况 Counter<out Number>var counter: Counter<out Number>
类型声明上界 注意这里是T extend Animal ,和带? extend Animal 的声明不是一个东西
java
1 2 3 class Monster <T extends Animal & Food>{ }
kotlin
1 2 3 4 class Monster <T : Animal >{} class Monster <T > where T : Animal , T : Food {}
refield java
1 2 3 <T> void printIfTypeMatch (Object item) { if (item instanceof T){} }
kotlin
1 2 3 4 5 6 7 8 fun <T> void printIfTypeMatch (item : Any ) { if (item instanceof T){} }inline fun <reified T> void printIfTypeMatch (item : Any ) { if (item instanceof T){} }
Java泛型类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Order <T> { String orderName; int orderId; T orderT; public Order () { } public Order (String orderName, int orderId, T orderT) { this .orderName = orderName; this .orderId = orderId; this .orderT = orderT; } public T getOrderT () { return orderT; } public void setOrderT (T orderT) { this .orderT = orderT; } }
子类不是泛型类 父类指明类型,由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型。
1 2 3 4 5 6 7 8 9 public class SubOrder extends Order <Integer> { }@Test public void test1 () { SubOrder subOrder = new SubOrder (); subOrder.setOrderT(1122 ); }
子父类关系 类A是类B的父类,G和G二者不具备父子类关系,也不具任何关系。
类A是类B的父类,A是 B的父类。
反证法:
https://www.bilibili.com/video/BV1fi4y1b7NM?p=11
子类是泛型类 1 2 3 4 5 public class SubOrder1 <T> extends Order <T> { }SubOrder1 <String> sub2 = new SubOrder1 <>(); sub2.setOrderT("order..." );
泛型方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Person3 <T> { public void show (T name) { System.out.println(name + "正在演讲" ); } public <M> void show1 (M name) { System.out.println(name + "正在演讲" ); } public static <W> void show2 (W name) { System.out.println(name + ":静态方法正在演讲!" ); } public static <E> E show3 (E name) { return name; } }
泛型接口 1 2 3 interface PerInt <T > { fun show (name: T ) }
泛型接口的实现类 可以指定具体的泛型接口的具体泛型类型
1 2 3 4 class PerImtImpl : PerInt <String > { override fun show (name: String ) { } }
泛型接口的实现类,如果没有指定具体的泛型类型,必须要在这个实现类中声明一个泛型类型的占位符给接口用
1 2 3 4 class PerImtImpl01 <T > : PerInt <T > { override fun show (name: T ) { } }
类型擦除 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
泛型的作用是能在编译期间就提示错误,而不是运行时。
1 2 3 4 val p1 = PerImtImpl01<String>()val p2 = PerImtImpl01<Int >() println(p1.javaClass == p2.javaClass) println(p1.javaClass === p2.javaClass)
通配符 Java中的继承 ,在泛型中并不是父子类关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class PerImtImpl01 <T > : PerInt <T > { var name: T get () { return name } set (value) { name = value } override fun show (name: T ) { } fun show (p: PerImtImpl01 <*>) { name = p.name as T } }
1 2 3 4 val p3 = PerImtImpl01<Number>()val p4 = PerImtImpl01<Int >() p3.name = 111 p4.show(p3)
符号区别 Java用 ? Kotlin 用 *
无界通配符 意味着可以使用任何对象,因此使用它类似于使用原生类型
类A是类B的父类,G和G的公共的父类是: G<?>
遍历List 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void test2 () { List <Object> list1 = null ; List <String> list2 = null ; List <?> list = null ; list = list1; list = list2; print(list1); print(list2); } public void print (List<?> list) { Iterator <?> iterator = list.iterator(); while (iterator.hasNext()){ Object o = iterator.next(); System.out.println(o); } list.forEach(o -> { System.out.println(o); }); }
对于List> 就不能向其添加数据。如果任何类型引用通过赋值给 List> 能添加数据,那定义泛型就没意义了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 List <?> list = null ; ArrayList <String> list3 = new ArrayList <>(); list3.add("AA" ); list3.add("BB" ); list3.add("CC" ); list = list3; list.add(null ); Object o = list.get(0 ); System.out.println(o);
上下界 如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public class StaticGenerator <T> { .... .... public static <T> void show (T t) { } }
上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。
下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景。
上界通配符 <? extends T> 泛型中的继承
泛型中的继承不是Java中的继承。也就是说java中的父子类关系, 在泛型中 并不是父子类关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Fruit { }class Apple extends Fruit { }class Plate <T > { T item; public Plate() { } public Plate(T item) { this.item = item; } public T get() { return item; } public void set(T item) { this.item = item; } }
? extends Fruit赋值 ,无法添加数据,不知道子类到底多小. ? extends Fruit 可以理解为 <= Fruit,
List<? extends Fruit> 可以作为 List , List的父类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 List <? extends Fruit> list4 = new ArrayList <Apple>(); List <? extends Fruit> list5 = new ArrayList <Fruit>(); Fruit fruit = list4.get(0 );
读取数据 ? extends Fruit 所以泛型最大的父类就是Fruit,子类型可以直接复制给父类
1 2 3 4 5 6 7 8 Fruit fruit = list4.get(0 );
你会发现无法往里面设置任何数据 ,按道理说我们将泛型类型设置为? 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]范围,-∞不知道具体的类型小到多少
下界通配符 <? super T>
? super Fruit赋值,只能添加数据 。 ? super Fruit 可以理解为 >= Fruit
1 2 3 4 5 6 7 8 9 10 List <? super Fruit> list6 = new ArrayList <Fruit>(); List <? super Fruit> list7 = new ArrayList <Object>(); Object object = list6.get(0 );
读取数据 1 2 3 Object object = list6.get(0 );
写入数据 ? super Fruit范围是是 [Fruit,+∞), 所以Fruit范围内的都可以添加
1 2 3 list6.add(new Fruit ()); list6.add(new Apple ());
下界通配符<? super Fruit>不影响往里面存储,但是读取出来的数据只能是Object类型。
下界通配符规定了元素最小的粒度,必须是Fruit或其基类,那么我往里面存储Fruit及其子类都是可以的,因为它都可以隐式的转化为Fruit类型。但是往外读就不好控制了,里面存储的都是Fruit及其基类,无法转型为任何一种类型,只有Object基类才能装下。
https://juejin.im/post/5b614848e51d45355d51f792
www.jianshu.com/p/dd34211f2565
https://itimetraveler.github.io/2016/12/27/%E3%80%90Java%E3%80%91%E6%B3%9B%E5%9E%8B%E4%B8%AD%20extends%20%E5%92%8C%20super%20%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F/
https://www.bilibili.com/video/BV1xJ411n77R?p=7
kotlin out in 和 extend super没关系
out 只能获取 不能修改
1 2 3 4 var list:MutableList<out Fruit> = ArrayList<Apple>() var list2: ArrayList<in Apple> = ArrayList<Fruit>()
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 9 ArrayList <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 19 public 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 6 FruitArr <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 2 3 Class <Person> personClass = Person.class;Constructor <Person> constructor = personClass.getConstructor();Person person = constructor.newInstance();
https://www.bilibili.com/video/BV1xJ411n77R?p=12
$i$f$withM 有时候看到$以为是操作符,其实就是$i$f$withM整体的变量
1 2 3 Array <out Any > 对应于 Java 的 Array <? extends Object >Array <in String > 对应于 Java 的 Array <? super String >