相关链接

  • Excel目录

目录

  • P18 【泛型】各类泛型对象、通配符、类型擦除
    • 1 什么是泛型
    • 2 泛型类、接口库
      • 2.1 泛型类定义语法
      • 2.2 常用泛型标识
      • 2.3 使用语法
      • 2.4 泛型类注意事项
      • 2.5 案例:泛型类-使用
      • 2.6 泛型类-子类
      • 2.7 泛型类-接口
    • 3 泛型方法⭐️
    • 4 类型通配符 ? ⭐️
      • 4.1 类型通配符的使用
      • 4.2 类型通配符上限
      • 4.3 类型通配符下限
        • 4.3.1 TreeSet的通配符下限
    • 5 类型擦除
      • 5.1 擦除类中定义的参数
      • 5.2 擦除方法中类型定义的参数
    • 6 泛型和数组
    • 7 泛型和反射
    • 8 可变长参数 ...

P18 【泛型】各类泛型对象、通配符、类型擦除


1 什么是泛型


背景: Java推出泛型之前,可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要明确指定存储的每个元素的数据类型,否则很容易引发ClassCastException异常。

概念: Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构(类型安全)。

   泛型的本质就是参数化类型,也就是所操作的数据局类型被指定为一个参数,消除了强制类型转换异常的风险。

泛型类: 实例化类的时候指明泛型的具体类型

泛型方法: 调用方法的时候指明泛型的具体类型

类型擦除: JDK5之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。这是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,称之为–类型擦除。


案例代码一  泛型产生的背景

package com.groupies.base.day18;import java.util.ArrayList;/*** @author GroupiesM* @date 2021/06/04* @introduction 泛型产生的背景** 泛型:*     1.类型安全*     2.消除了强制类型转换异常的风险** 形参:E*      public class ArrayList<E> extends AbstractList<E>* 实参:String*     ArrayList<String> strList = new ArrayList<>();*/
public class Demo1Type {public static void main(String[] args) {ArrayList<Object> list = new ArrayList<>();list.add("java");list.add(100);list.add(true);for (Object o : list) {String s = null;try {s = (String) o;} catch (ClassCastException e) {//Exception in thread "main" java.lang.ClassCastException .....//e.printStackTrace();}System.out.println(s);//java null null}/** 泛型:*      1.编译期间检查类型*      2.减少了数据类型转换*/ArrayList<String> strList = new ArrayList<>();strList.add("a");strList.add("b");//strList.add(5);for (String s : strList) {System.out.println(s);//a b}ArrayList<Integer> intList = new ArrayList<>();intList.add(100);//自动装箱intList.add(200);//intList.add("300");for (Integer integer : intList) {int num = integer;//自动拆箱System.out.println(num);//100 200}}
}

2 泛型类、接口库


泛型类: 实例化类的时候指明泛型的具体类型


2.1 泛型类定义语法


class 类名称<泛型标识,泛型标识,...>{private 泛型标识 变量名;....
}//实例
class Generic<T>{private T key;....
}

2.2 常用泛型标识


TEKV :四种标识符都代表泛型,没有任何区别,在使用多个参数时,可以用来区分各个泛型参数。


2.3 使用语法


类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
类名<具体的数据类型> 对象名 = new 类名<>();//Java1.7以后,后面的<>中的具体的数据类型可以省略不写//实例
Generic<String> 对象名 = new Generic<>();

2.4 泛型类注意事项


  • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
  • 泛型的类型参数只能是类类型,不能是基本数据类型
  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型

案例代码二  定义一个泛型类 【a.泛型类】

package com.groupies.base.day18;/*** @author GroupiesM* @date 2021/06/04* @introduction 定义一个泛型类 泛型类* @param <T> 泛型标识--类型形参*         T 创建对象的时候这里制订具体的数据类型--类型实参*/
public class Demo2Type<T> {//T 是由外部使用本类的时候来指定的private T key;public Demo2Type() {}public Demo2Type(T key) {this.key = key;}public T getKey() {return key;}public void setKey(T key) {this.key = key;}
}

案例代码二  定义一个泛型类 【b.测试类】

package com.groupies.base.day18;/*** @author GroupiesM* @date 2021/06/07* @introduction 定义一个泛型类-测试类**  1.泛型类在创建的时候,指定操作的具体类型*  2.泛型类在创建对象的时候,没有指定类型,将按照Object类型来操作*  3.泛型类,不支持基本数据类型*  4.同一泛型类,根据不同的数据类型,创建的对象,本质上是同一类型*/
public class Demo2Test {public static void main(String[] args) {//1.泛型类在创建的时候,指定操作的具体类型Demo2Type<String> strType = new Demo2Type<>("abc");String key1 = strType.getKey();System.out.println("key1:" + key1);System.out.println("------------------------------");Demo2Type<Integer> integerType = new Demo2Type<>(5);Integer key2 = integerType.getKey();System.out.println("key2:" + key2);System.out.println("------------------------------");//2.泛型类在创建对象的时候,没有指定类型,将按照Object类型来操作Demo2Type objType = new Demo2Type<>("ABC");Object key3 = objType.getKey();System.out.println("key3:" + key3);//3.泛型类,不支持基本数据类型//Demo2Type<int> inteType = new Demo2Type<>(5);System.out.println("------------------------------");//4.同一泛型类,根据不同的数据类型,创建的对象,本质上是同一类型System.out.println(strType.getClass());//class com.groupies.base.day18.Demo2TypeSystem.out.println(integerType.getClass());//class com.groupies.base.day18.Demo2TypeSystem.out.println(objType.getClass());//class com.groupies.base.day18.Demo2Type}
}

2.5 案例:泛型类-使用


  • 需求:通过泛型实现一个公司年会抽奖功能

案例代码三  泛型类-使用 【a.泛型类】

package com.groupies.base.day18;import java.util.ArrayList;
import java.util.Random;/*** @author GroupiesM* @date 2021/06/07* @introduction 抽奖器 泛型类*/
public class Demo3ProductGetter<T> {Random r = new Random();//随机抽奖private T product;//奖品ArrayList<T> list = new ArrayList<>();//奖品池/*** @introduction 添加奖品* @param t*/public void addProduct(T t) {list.add(t);}//抽奖public T getProduct() {T remove = list.remove(r.nextInt(list.size()));//随机移除一个奖品return remove;}//显示奖品池public String show() {StringBuilder sb = new StringBuilder();for (int i = 0; i < list.size(); i++) {sb.append(list.get(i));if (i!=list.size()-1){sb.append("、");}}return sb.toString();}
}

案例代码三  泛型类-使用 【b.测试类】

package com.groupies.base.day18;import java.util.Scanner;/*** @author GroupiesM* @date 2021/06/07* @introduction*/
public class Demo3Main {//创建抽奖器对象,指定数据类型static Demo3ProductGetter<String> stringProductGetter = new Demo3ProductGetter<>();static Demo3ProductGetter<Integer> integerProductGetter = new Demo3ProductGetter<>();//填充奖池static {int[] intProdudcts = {1000, 5000, 3000, 300,30000000};for (int i = 0; i < intProdudcts.length; i++) {integerProductGetter.addProduct(intProdudcts[i]);}String[] strProducts = {"苹果手机", "华为手机", "扫地机器人", "咖啡机"};for (int i = 0; i < strProducts.length; i++) {stringProductGetter.addProduct(strProducts[i]);}}public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (stringProductGetter.list.size() > 0 | integerProductGetter.list.size() > 0) {//while (true) {System.out.print("剩余【物品】奖品:" + stringProductGetter.show() + "\r\n");System.out.print("剩余【现金】奖品:" + integerProductGetter.show() + "\r\n");System.out.println("请输入抽奖类型,1:物品 2:现金");int i = sc.nextInt();if (i == 1 && stringProductGetter.list.size() > 0) {getPrize();} else if (i == 2 && integerProductGetter.list.size() > 0) {getCash();} else {System.out.println("-------------");System.out.println("抽奖失败,请重新输入");System.out.println("-------------");}}}//抽取奖品private static void getPrize() {//抽取奖品String product = stringProductGetter.getProduct();System.out.println("-------------");System.out.println("恭喜您,您抽到了" + product);System.out.println("-------------");if (stringProductGetter.list.size() == 0 && integerProductGetter.list.size() == 0){System.out.println("已抽完全部奖品,活动结束");}}//抽取现金private static void getCash() {//抽取奖品Integer product = integerProductGetter.getProduct();System.out.println("-------------");System.out.println("恭喜您,您抽到了" + product + "元");System.out.println("-------------");if (stringProductGetter.list.size() == 0 && integerProductGetter.list.size() == 0){System.out.println("已抽完全部奖品,活动结束");}}
}

2.6 泛型类-子类


  • a. 子类是泛型类,子类和父类的泛型类型要一致

  • b. 子类不是泛型类,父类要明确泛型的数据类型

//a. 子类也是泛型类,子类和父类的泛型类型要一致public class Parent<E>// class ChildGeneric<E> extends Generic<T> //报错// class ChildGeneric<T,E> extends Generic<T,E> //报错class ChildGeneric<T> extends Generic<T>class ChildGeneric<E> extends Generic<E>class ChildGeneric<T,E,K,V> extends Generic<T>//b. 子类不是泛型类,父类要明确泛型的数据类型class ChildGeneric extends Generic<String>

案例代码四  泛型类-子类 【a.泛型父类】

package com.groupies.base.day18.demo4;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型父类*/
public class Parent<E> {private E value;public E getValue() {return value;}public void setValue(E value) {this.value = value;}
}

案例代码四  泛型类-子类 【b.泛型子类1】

package com.groupies.base.day18.demo4;/*** @author GroupiesM* @date 2021/06/07* @introduction** a. 子类是泛型类,子类和父类的泛型类型要一致*/
//public class ChildFirst<T> extends Parent <E>{//报错
public class ChildFirst<T> extends Parent <T>{@Overridepublic T getValue() {return super.getValue();}
}

案例代码四  泛型类-子类 【b.泛型子类2】

package com.groupies.base.day18.demo4;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型子类 子类2** b.子类不是泛型类,父类要明确泛型的数据类型*/
public class ChiledSecond extends Parent<Integer> {@Overridepublic Integer getValue() {return super.getValue();}@Overridepublic void setValue(Integer value) {super.setValue(value);}
}

案例代码四  泛型类-子类 【c.测试类】

package com.groupies.base.day18.demo4;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型子类 测试类*/
public class Test {public static void main(String[] args) {//a. 子类是泛型类,子类和父类的泛型类型要一致ChildFirst<String> childFirst = new ChildFirst<>();childFirst.setValue("abc");String value1 = childFirst.getValue();System.out.println(value1);System.out.println("------------------------------");//b.子类不是泛型类,父类要明确泛型的数据类型ChiledSecond childSecond = new ChiledSecond();childSecond.setValue(100);Integer value2 = childSecond.getValue();System.out.println(value2);}
}

2.7 泛型类-接口


  • a. 实现类是泛型类,实现类和接口的泛型类型要一致

  • b. 实现类不是泛型类,接口要明确数据类型


案例代码五  泛型类-接口 【a.泛型接口】

package com.groupies.base.day18.demo5;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型接口*/
public interface Generator<T> {T getKey();
}

案例代码五  泛型类-接口 【b.泛型接口实现1】

package com.groupies.base.day18.demo5;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型接口 实现2** a. 实现类是泛型类,实现类和接口的泛型类型要一致*/
public class AppleT<T, E> implements Generator<T> {private T key ;private E value ;public AppleT() {}public AppleT(T key, E value) {this.key = key;this.value = value;}@Overridepublic T getKey() {return key;}public E getValue(){return value;}
}

案例代码五  泛型类-接口 【b.泛型接口实现2】

package com.groupies.base.day18.demo5;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型接口 实现1** b. 实现类不是泛型类,接口要明确数据类型*/
public class AppleString implements Generator<String>{@Overridepublic String getKey() {return "hello generic";}
}

案例代码五  泛型类-接口 【c.泛型测试】

package com.groupies.base.day18.demo5;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型接口 测试*/
public class Test {public static void main(String[] args) {AppleString appleString = new AppleString();String key1 = appleString.getKey();System.out.println(key1);//hello genericAppleT<String, Integer> appleT = new AppleT<>("苹果",5);String key = appleT.getKey();Integer value = appleT.getValue();System.out.println(key + "==" + value);//苹果==5}
}

3 泛型方法⭐️


泛型方法: 调用方法的时候指明泛型的具体类型

a.调用泛型方法:public <E> E getProduct(ArrayList<E> arr)

b.调用静态泛型方法:public static <T, E> void printType(T t, E e)

c.泛型方法的可变参数:public static <E> void print(E...e) ⭐️

总结: 泛型方法能使方法独立于类而产生的变化,如果static方法要使用泛型能力,就必须使其成为泛型方法。


  • 语法

    修饰符<T,E,...> 返回值类型 方法名(形参列表){方法体...
    }//a.调用泛型方法
    public <E> E getProduct(ArrayList<E> arr) {return arr.get(r.nextInt(arr.size()));
    }//b.调用静态泛型方法
    public static <T, E> void printType(T t, E e) {System.out.println(t + "\t" + t.getClass().getSimpleName());System.out.println(e + "\t" + e.getClass().getSimpleName());
    }//c.泛型方法的可变参数
    public static <E> void print(E...e){for (E e1 : e) {System.out.println(e1);}
    }
    

  • 注意事项

    • public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法
    • 只有声明了<T>的方法才是泛型方法,泛型类中使用了泛型的成员方法并不是泛型方法
    • <T>表名该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
    • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

案例代码六  泛型方法 【a.泛型类】

package com.groupies.base.day18.demo06;import java.util.ArrayList;
import java.util.Random;/*** @author GroupiesM* @date 2021/06/07* @introduction 抽奖器 泛型类*/
public class ProductGetter<T> {Random r = new Random();//随机抽奖private T product;//奖品ArrayList<T> list = new ArrayList<>();//奖品池/*** @introduction 添加奖品* @param t*/public void addProduct(T t) {list.add(t);}//抽奖public T getProduct() {T remove = list.remove(r.nextInt(list.size()));//随机移除一个奖品return remove;}//显示奖品池public String show() {StringBuilder sb = new StringBuilder();for (int i = 0; i < list.size(); i++) {sb.append(list.get(i));if (i != list.size() - 1) {sb.append("、");}}return sb.toString();}/*** @introduction a.调用泛型方法* @param arr* @param <E> 泛型标识,表示泛型方法,具体类型在调用方法时指定* @return E 泛型类型*/public <E> E getProduct(ArrayList<E> arr) {return arr.get(r.nextInt(arr.size()));}/*** @introduction b.调用静态泛型方法* @param t* @param e* @param k* @param <T> 泛型标识* @param <E> 泛型标识* @param <K> 泛型标识*/public static <T, E, K> void printType(T t, E e, K k) {System.out.println(t + "\t" + t.getClass().getSimpleName());System.out.println(e + "\t" + e.getClass().getSimpleName());System.out.println(k + "\t" + k.getClass().getSimpleName());}/*** @introduction c.泛型方法的可变参数* @param e* @param <E> 泛型标识*/public static <E> void print(E...e){for (E e1 : e) {System.out.println(e1);}}
}

案例代码六  泛型方法 【b.测试类】

package com.groupies.base.day18.demo06;import java.util.ArrayList;/*** @author GroupiesM* @date 2021/06/07* @introduction 泛型方法的使用**  a.调用泛型方法*  b.调用静态泛型方法*  c.泛型方法的可变参数*/
public class Test {public static void main(String[] args) {ProductGetter<Integer> productGetter = new ProductGetter<>();ArrayList<String> strList = new ArrayList<>();strList.add("笔记本电脑");strList.add("苹果手机");strList.add("扫地机器人");//a.调用泛型方法,类型是通过调用方法的时候来指定的String product1 = productGetter.getProduct(strList);//XXX StringSystem.out.println(product1 + "\t" + product1.getClass().getSimpleName());System.out.println("-------------------------------");ArrayList<Integer> integerList = new ArrayList<>();integerList.add(30);integerList.add(50);integerList.add(80);//a.调用泛型方法,类型是通过调用方法的时候来指定的Integer product2 = productGetter.getProduct(integerList);//XXX IntegerSystem.out.println(product2 + "\t" + product2.getClass().getSimpleName());System.out.println("-------------------------------");/** b.调用静态泛型方法* 100 Integer* java   String* true    Boolean*/ProductGetter.printType(100, "java", true);System.out.println("-------------------------------");/** c.泛型方法的可变参数* 1* a* true* []* [I@63961c42*/ProductGetter.print(1, "a", true, new ArrayList<>(), new int[3]);}
}

4 类型通配符 ? ⭐️


  • 类型通配符: 类型通配符一般是使用"?"代替具体的类型实参,所以,类型通配符是类型实参,而不是类型形参

4.1 类型通配符的使用


案例代码七  类型通配符 【a.泛型类】

package com.groupies.base.day18.demo07;/*** @author GroupiesM* @date 2021/06/07* @introduction 类型通配符 泛型类*/
public class Box <E>{private E first;public E getFirst() {return first;}public void setFirst(E first) {this.first = first;}
}

案例代码七  类型通配符 【b.测试类】

package com.groupies.base.day18.demo07;/*** @author GroupiesM* @date 2021/06/07* @introduction 类型通配符 测试类*/
public class Test {public static void main(String[] args) {Box<Number> box1 = new Box<>();box1.setFirst(100);showBox(box1);//Integer继承自number,在showBox方法传入Number子类对象不能触发多态Box<Integer> box2 = new Box<>();box2.setFirst(200);showBox(box2);//Box<Number>接收Integer时 => java: 不兼容的类型}/*public static void showBox(Box<Number> box){Number first = box.getFirst();System.out.println(first);}*///both method have same erasure 参数类型都为Box,所以不能触发重载/*public static void showBox(Box<Integer> box){}*///解决方案-> 泛型通配符 ? 代表 任意类型public static void showBox(Box<?> box){Object first = box.getFirst();System.out.println(first);}
}

4.2 类型通配符上限


  • 语法

    类/接口<? extends 上限实参类型> //要求该泛型的类型,只能是指定实参类型,或指定实参类型的子类类型//通配符上限实例
    public static void showAnimal(ArrayList<? extends Cat> list) {for (Cat cat : list) {System.out.println(cat);}
    }
    

案例代码八  类型通配符上限 【a.Animal类】

package com.groupies.base.day18.demo08;public class Animal { }

案例代码八  类型通配符上限 【a.Cat类】

package com.groupies.base.day18.demo08;public class Cat extends Animal{ }

案例代码八  类型通配符上限 【a.MiniCat类】

package com.groupies.base.day18.demo08;public class MiniCat extends Cat { }

案例代码八  类型通配符上限 【b.测试类】

package com.groupies.base.day18.demo08;import java.util.ArrayList;/*** @author GroupiesM* @date 2021/06/07* @introduction 类型通配符 测试类*/
public class Test {public static void main(String[] args) {ArrayList<Animal> animals = new ArrayList<>();ArrayList<Cat> cats = new ArrayList<>();ArrayList<MiniCat> miniCats = new ArrayList<>();//showAnimal(animals); //超出类型通配符上限,编译不通过showAnimal(cats);showAnimal(miniCats);//public boolean addAll(Collection<? extends E> c)//cats.addAll(animals); //超出类型通配符上限,编译不通过cats.addAll(cats);cats.addAll(miniCats);}/*** @introduction 指定通配符上限*              泛型上线通配符,传递的集合类型,只能是Cat或Cat的子类* @param list*/public static void showAnimal(ArrayList<? extends Cat> list) {//list.add(new Cat());      //类型通配符上限 =>不能添加集合元素//list.add(new MiniCat());  //类型通配符上限 =>不能添加集合元素for (Cat cat : list) {System.out.println(cat);}}
}

4.3 类型通配符下限


  • 语法

    类/接口<? extends 上限实参类型> //要求该泛型的类型,只能是指定实参类型,或指定实参类型的父类类型//通配符下限实例
    public static void showAnimal(ArrayList<? super Cat> list) {for (Cat cat : list) {System.out.println(cat);}
    }  

案例代码九  类型通配符下限 【a.Animal类】

package com.groupies.base.day18.demo09;public class Animal { }

案例代码九  类型通配符下限 【a.Cat类】

package com.groupies.base.day18.demo09;public class Cat extends Animal{ }

案例代码九  类型通配符下限 【a.MiniCat类】

package com.groupies.base.day18.demo09;public class MiniCat extends Cat { }

案例代码九  类型通配符下限 【b.测试类】

package com.groupies.base.day18.demo09;import java.util.ArrayList;/*** @author GroupiesM* @date 2021/06/07* @introduction 类型通配符 测试类*/
public class Test {public static void main(String[] args) {ArrayList<Animal> animals = new ArrayList<>();ArrayList<Cat> cats = new ArrayList<>();ArrayList<MiniCat> miniCats = new ArrayList<>();showAnimal(animals);showAnimal(cats);//showAnimal(miniCats); //超出类型通配符下限,编译不通过}/*** @introduction 指定通配符下限*              泛型下限通配符,传递的集合类型,只能是Cat或Cat的父类* @param list*/public static void showAnimal(ArrayList<? super Cat> list) {//list.add(new Animal());list.add(new Cat());      //类型通配符下限 =>可以添加Cat或Cat子类集合元素list.add(new MiniCat());  //类型通配符下限 =>可以添加Cat或Cat子类集合元素for (Object o : list) {System.out.println(o);}}
}

4.3.1 TreeSet的通配符下限


案例代码十  TreeSet的通配符下限 【a.Animal类】

package com.groupies.base.day18.demo10;public class Animal {public String name;public Animal(String name) {this.name = name;}@Overridepublic String toString() {return "Animal{" +"name='" + name + '\'' +'}';}
}

案例代码十  TreeSet的通配符下限 【a.Cat类】

package com.groupies.base.day18.demo10;public class Cat extends Animal {public int age;public Cat(String name, int age) {super(name);this.age = age;}@Overridepublic String toString() {return "Cat{" +"name='" + name + '\'' +", age=" + age +'}';}
}

案例代码十  TreeSet的通配符下限 【a.MiniCat类】

package com.groupies.base.day18.demo10;public class MiniCat extends Cat {public int level;public MiniCat(String name, int age, int level) {super(name, age);this.level = level;}@Overridepublic String toString() {return "MiniCat{" +"name='" + name + '\'' +", age=" + age +", level=" + level +'}';}
}

案例代码十  TreeSet的通配符下限 【b.测试类】

package com.groupies.base.day18.demo10;import java.util.Comparator;
import java.util.TreeSet;/*** @author GroupiesM* @date 2021/06/08* @introduction*/
public class Test {public static void main(String[] args) {TreeSet<Cat> treeSet2 = new TreeSet<>(new Comparator1());treeSet2.add(new Cat("jerry", 20));treeSet2.add(new Cat("amy", 22));treeSet2.add(new Cat("frank", 22));treeSet2.add(new Cat("frank", 35));for (Cat cat : treeSet2) {/** comparator2比较姓名:o1.name.compareTo(o2.name);  按姓名排序,且frank添加失败* Cat{name='amy', age=22}* Cat{name='frank', age=22}* Cat{name='jerry', age=20}*/System.out.println(cat);}System.out.println("-----------------------------");/*** public TreeSet(Comparator<? super E> comparator) {*    this(new TreeMap<>(comparator));* }*/TreeSet<Cat> treeSet1 = new TreeSet<>(new Comparator2());treeSet1.add(new Cat("jerry", 20));treeSet1.add(new Cat("amy", 22));treeSet1.add(new Cat("frank", 35));treeSet1.add(new Cat("jim", 35));for (Cat cat : treeSet1) {/** comparator2比较年龄:o1.age - o2.age;  按年龄排序,且jim添加失败* Cat{name='jerry', age=20}* Cat{name='amy', age=22}* Cat{name='frank', age=35}*/System.out.println(cat);}/**Comparator<? super E> comparator* 下限 E = Cat* Comparator3,传入*///TreeSet<Cat> treeSet3 = new TreeSet<Cat>(new Comparator3());}
}//外部比较器1
class Comparator1 implements Comparator<Animal> {@Overridepublic int compare(Animal o1, Animal o2) {//如果参数字符串等于此字符串,则返回值 0;//如果此字符串按字典顺序小于字符串参数,则返回一个小于 0 的值;//如果此字符串按字典顺序大于字符串参数,则返回一个大于 0 的值。return o1.name.compareTo(o2.name);}
}//外部比较器2
class Comparator2 implements Comparator<Cat> {@Overridepublic int compare(Cat o1, Cat o2) {return o1.age - o2.age;}
}//外部比较器3
class Comparator3 implements Comparator<MiniCat> {@Overridepublic int compare(MiniCat o1, MiniCat o2) {return o1.level - o2.level;}
}

5 类型擦除


类型擦除: JDK5之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。这是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,称之为–类型擦除。


案例代码十一  类型擦除

public class Demo11Test {public static void main(String[] args) {ArrayList<Integer> intList = new ArrayList<>();ArrayList<String> strList = new ArrayList<>();//Integer 和 String两种泛型的信息都被擦除,只剩下ArrayList信息System.out.println(intList.getClass().getSimpleName());//ArrayListSystem.out.println(strList.getClass().getSimpleName());//ArrayListSystem.out.println(intList.getClass() == strList.getClass());//true}
}

5.1 擦除类中定义的参数


1️⃣无限制类型擦除:

2️⃣有限制类型擦除:


案例代码十二  【类】有限制类型擦除 【a.泛型类】

package com.groupies.base.day18.demo12;/*** @author GroupiesM* @date 2021/06/08* @introduction 【类】有限制类型擦除 泛型类*/
public class Erasure<T extends Number> {private T key;public T getKey() {return key;}public void setKey(T key) {this.key = key;}
}

案例代码十二  【类】有限制类型擦除 【b.测试类】

package com.groupies.base.day18.demo12;import java.lang.reflect.Field;/*** @author GroupiesM* @date 2021/06/08* @introduction 【类】有限制类型擦除 测试类*/
public class Test {public static void main(String[] args) {//创建实例 -> 泛型使用Integer类Erasure<Integer> intErasure = new Erasure<>();//利用反射 获取Erasure类的字节码文件的Class类对象Class<? extends Erasure> cls = intErasure.getClass();//获取字段Field[] declaredFields = cls.getDeclaredFields();for (Field declaredField : declaredFields) {/** 打印成员变量的 字段名称 + 字段类型  => Integer类型擦除为Number(Erasure通配符上限)* key:Number */System.out.println(declaredField.getName() + ":" + declaredField.getType().getSimpleName());}}
}

5.2 擦除方法中类型定义的参数

1️⃣无限制类型擦除:

2️⃣有限制类型擦除:

3️⃣桥接方法​:类型擦除和多态发生了冲突,为了解决这个问题,编译期会产生一个桥方法,在虚拟机中会由参数和返回值类型不同而产生的两个不同的字节码文件,但虚拟机能够正确处理这种情况,通过桥接方法,保持接口和类的实现关系。


案例代码十三  【方法】有限制类型擦除 【a.泛型类】

package com.groupies.base.day18.demo13;import java.util.List;/*** @author GroupiesM* @date 2021/06/08* @introduction 【方法】类型擦除 泛型类*/
public class Erasure<T extends Number> {private T key;public T getKey() {                             //无限制类型擦除 类型通配符上限:Numberreturn key;}public void setKey(T key) {                      //无返回值 voidthis.key = key;}public <T extends List> T show1(T t){ return t;}   //无限制类型擦除 类型通配符上限:Listpublic <T> T show2(T t){ return t;}                 //无限制类型擦除
}

案例代码十三  【方法】有限制类型擦除 【b.测试类】

package com.groupies.base.day18.demo13;import java.lang.reflect.Method;/*** @author GroupiesM* @date 2021/06/08* @introduction 【方法】类型擦除 测试类*/
public class Test {public static void main(String[] args) {//利用反射 获取Erasure类的字节码文件的Class类对象Class<? extends Erasure> cls = Erasure.class;//获取方法Method[] declaredMethods = cls.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {/** 打印成员方法的 方法名称 + 方法返回值类型  => Integer类型擦除为Number(Erasure通配符上限)* getKey:Number* setKey:void* show1:List* show2:Object */System.out.println(declaredMethod.getName() + ":" + declaredMethod.getReturnType().getSimpleName());}}
}

3️⃣桥接方法​:类型擦除和多态发生了冲突,为了解决这个问题,编译期会产生一个桥方法,在虚拟机中会由参数和返回值类型不同而产生的两个不同的字节码文件,但虚拟机能够正确处理这种情况,通过桥接方法,保持接口和类的实现关系。


案例代码十四  验证桥接方法的存在 【a.泛型接口】

package com.groupies.base.day18.demo14;public interface Info<T> {T info(T t);
}

案例代码十四  验证桥接方法的存在 【b.实现类】

package com.groupies.base.day18.demo14;public class InfoImpl implements Info<Integer> {@Overridepublic Integer info(Integer value) {return value;}
}/*
//类型擦除后效果如下
public class InfoImpl implements Info {public Integer info(Integer value) {return value;}@Overridepublic Object info(Object value) {return info((Integer) value);}
}
*/

案例代码十四  验证桥接方法的存在 【c.测试类】

package com.groupies.base.day18.demo14;import java.lang.reflect.Method;/*** @author GroupiesM* @date 2021/06/08* @introduction 桥接方法 实现类*/
public class Test {public static void main(String[] args) {//利用反射 获取Erasure类的字节码文件的Class类对象Class<InfoImpl> cls = InfoImpl.class;//获取类中所有方法Method[] declaredMethods = cls.getDeclaredMethods();//通过反射获取InfoImpl类的所有方法,查看类型擦除后是否有两个方法(一个@Override重写方法,一个桥接方法)for (Method declaredMethod : declaredMethods) {/** 打印成员方法的 方法名称 + 方法返回值类型  => Integer为定义的类型,Object为类型擦除生成的桥接方法* info:Integer* info:Object* */System.out.println(declaredMethod.getName() + ":" + declaredMethod.getReturnType().getSimpleName());}}
}


6 泛型和数组


创建方式:

  a.可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象

  b.可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[]数组

⚠️建议: 不要使用泛型数组,可以使用泛型集合代替泛型数组的功能


案例代码十五  泛型数组的创建A方式

package com.groupies.base.day18.demo15;import java.util.ArrayList;/*** @author GroupiesM* @date 2021/06/08* @introduction 泛型数组的创建A方式**  a.可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象**  关于A不能创建带泛型的数组对象:*      泛型通过类型擦除,只保留到编译期;数组的初始化后,从编译器时期开始就一直持有类型;*       两者从设计理念上就是冲突的,所以jdk不允许直接创建带泛型的数组对象*/
public class TestA {public static void main(String[] args) {//ArrayList<String>[] listArr;//a.可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象//ArrayList<String>[] listArr1 = new ArrayList<>[5];    //报错ArrayList[] list = new ArrayList[5];ArrayList<String>[] listArr = list;ArrayList<Integer> intList = new ArrayList<>();intList.add(100);ArrayList<String> strList = new ArrayList<>();strList.add("abc");list[0] = intList;//在泛型ArrayList数组中(listArr)指向了一个原生类型数组对象(list),list中可以放入Integer类型,所以会发生类型转换错误try {//ClassCastException: java.lang.Integer cannot be cast to java.lang.StringString s = listArr[0].get(0);System.out.println(s);} catch (ClassCastException e) {e.printStackTrace();}//采用以下方式创建ArrayList<String>[] listArr2 = new ArrayList[5];//listArr2[0] = intList; //泛型检查 编译不通过listArr2[0] = strList;String s2 = listArr2[0].get(0);System.out.println(s2);//abc}
}

案例代码十六  泛型数组的创建B方式 【a.工具类】

package com.groupies.base.day18.demo16;import java.lang.reflect.Array;/*** @author GroupiesM* @date 2021/06/08* @introduction 泛型数组B 工具类** b.可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[]数组*/
public class Fruit<T> {//private T[] array = new T[3];private T[] array;/**** @param cls 类对象* @param length 数组长度*/public Fruit(Class<T> cls, int length) {//通过Array.newInstance创建泛型数组,并强制转换array = (T[]) Array.newInstance(cls, length);}/*** @introduction 泛型数组填充元素* @param index 指定数组索引* @param item 指定填充元素*/public void put(int index,T item){array[index] = item;}/*** @introduction 泛型数组获取数组元素* @param index 指定数组索引* @return 返回指定元素*/public T get(int index){return array[index];}/*** @introduction 泛型数组获取所有元素* @return 数组所有元素*/public T[] getArray(){return array;}
}

案例代码十六  泛型数组的创建B方式 【b.测试类】

package com.groupies.base.day18.demo16;import java.util.Arrays;/*** @author GroupiesM* @date 2021/06/08* @introduction 泛型数组B 测试类**  b.可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[]数组*/
public class TestB {public static void main(String[] args) {Fruit<String> stringFruit = new Fruit<String>(String.class,5);Fruit<Integer> intFruit = new Fruit<Integer>(Integer.class,5);stringFruit.put(0,"苹果");stringFruit.put(1,"西瓜");intFruit.put(0,10);intFruit.put(5,20);String[] strArr = stringFruit.getArray();Integer[] intArr = intFruit.getArray();String s1 = Arrays.toString(strArr);String s2 = Arrays.toString(intArr);System.out.println(s1);//[苹果, 西瓜, null, null, null]System.out.println("-------------");System.out.println(s2);//[10, 20, null, null, null]}
}

7 泛型和反射


反射常用的泛型类

  Class<T>

  Constructor<T>


案例代码十七  反射常用的泛型类 【b.测试类】

package com.groupies.base.day18.demo17;import java.lang.reflect.Constructor;/*** @author GroupiesM* @date 2021/06/08* @introduction 反射常用的泛型类**  采用泛型反射类,编写代码时更方便*/
public class Test {public static void main(String[] args) throws ReflectiveOperationException {Class<Person> personClass1 = Person.class;//默认返回了泛型为Person的Class类对象Constructor<Person> constructor1 = personClass1.getConstructor();//默认返回了泛型为Person的Constructor构造器对象Person person1 = constructor1.newInstance();Class personClass2 = Person.class;Constructor constructor2 = personClass2.getConstructor();Object o = constructor2.newInstance();//当constructor不指定泛型时,默认返回的是Object类型对象}
}

8 可变长参数 …

  一个方法只能有一个可变长参数,并且这个可变长参数必须是该方法的最后一个参数。

public class TestC {@Testpublic void fun1() {t1(1,"a","b");String[] a="a,b,c,d".split(",");t1(1,a);}private void t1(int num, String ... var){for (String s : var) {System.out.println(s);}}
}

21/06/08

M

3.1_19 JavaSE入门 P18 【泛型】各类泛型对象、通配符、类型擦除相关推荐

  1. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    原 java泛型(二).泛型的内部原理:类型擦除以及类型擦除带来的问题 2012年08月29日 23:44:10 Kilnn 阅读数:56717 版权声明:本文为博主原创文章,未经博主允许不得转载. ...

  2. 泛型(派生子类,泛型通配符,类型擦除)

    目录 从泛型类派生子类 1.子类不是泛型类,明确父类类型 2.子类和父类都是泛型类 泛型通配符 1.类型通配符上限 2.类型通配符下限 类型擦除 从泛型类派生子类 1.子类不是泛型类,明确父类类型 c ...

  3. 一句话,讲清楚java泛型的本质(非类型擦除)

    ?欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 背景 昨天,在逛论坛时遇到个这么个问题,上代码: public class GenericT ...

  4. 泛型中的类型擦除和桥方法

    在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程.为了实现泛型,Java编译器应用类型擦除实现: 1.  用类型参数(type parameters)的限定(如果没有就用Object ...

  5. java泛型的泛型_Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java ...

  6. java泛型-类型擦除

    2019独角兽企业重金招聘Python工程师标准>>> 最近了解了一下java的泛型,了解到了"类型擦除"这个东西,现做个简单小结. java泛型实现的原理可以说 ...

  7. Java基础篇:泛型与类型擦除

    一.什么是泛型: 泛型的本质是 参数化类型,也就是说 将所操作的数据类型 指定为一个参数,在不创建新类的情况下,通过参数来指定所要操作的具体类型(类似于方法中的变量参数,此时类型也定义成参数形式),也 ...

  8. Java泛型总结---基本用法,类型限定,通配符,类型擦除

    一.基本概念和用法 在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化.例如在哈希表的存取中,JDK1.5之前使用HashMap的 ...

  9. list 泛型_带你深挖Java泛型类型擦除以及类型擦除带来的问题

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:蜗牛大师 cnblogs.com/wuqinglong/p/9456193.html ...

最新文章

  1. 干货 | “青年AI自强计划项目”首节讲座视频、课件分享
  2. SharePoint 2010: 使用Visio Services展示SCOM数据
  3. MySQL防止库存超卖方法总结
  4. 【错误记录】Flutter / Android 报错 ( AAPT: error: attribute android:requestLegacyExternalStorage not found )
  5. 【One by One系列】IdentityServer4(一)OAuth2.0与OpenID Connect 1.0
  6. rowmapper_Spring Integration Jdbc RowMapper示例
  7. 【MFC系列2】Win32项目转换为MFC项目
  8. js实现复制粘贴功能
  9. ​《麻省理工科技评论》选出2019年全球十大突破性技术
  10. 损失层SoftmaxWithLossLayer
  11. olcd12864的u8g2库_U8G2 软件包单色1.3寸OLED屏驱动在 RT-Thread 移植问题
  12. 【转】js中forEach回调同异步问题
  13. 薛兆丰经济学讲义 简述
  14. (附源码)ssm教培管理系统 毕业设计 230932
  15. 你有全面了解过LIMS系统吗?
  16. js获取浏览器的高度
  17. VMware Workstation -- 破解密码
  18. 【愚人节小程序】Java Swing的简单使用
  19. 【CSS】绘制一个任意角度的扇形
  20. java 气泡_JAVA实现聊天气泡

热门文章

  1. servlet八大监听器
  2. 第一张多米诺 微软黑屏来了
  3. html三角形小图标,纯css实现全兼容三角形图标
  4. [软件人生]老板爽约后员工的心态考虑,对话诚信
  5. 2021年G1工业锅炉司炉答案解析及G1工业锅炉司炉考试资料
  6. ctf---小白篇2
  7. 劲爆!微信小程序可在附近的朋友圈展示啦!
  8. postgres远程连接方式配置
  9. 3、大话设计模式--浅谈基础
  10. 亚联发展和华为合作鸿蒙系统,华为鸿蒙系统将正式发布!A股软件板块被引爆 联络互动连续三个一字涨停...