【Java专题】Java泛型集合详解

tech2024-11-08  9

一、什么是泛型?

1、先来看不是泛型的ArrayList集合 ArrayList集合的底层是一个object[]数组,但是它跟数组比起来又有很多的优势,它可以存很多不同类型的数据。问题出现在数据被取出来的时候,强制转换引发ClassCastException异常。Collection虽然表面看可以存储各种类型的对象,其实际上它只能存储同一种类型的数据。 /** * @author Jason * @create 2020-07-11 10:57 * 普通集合存在的问题 */ public class GenericityTest01 {   public static void main(String[] args) {     Collection list = new ArrayList();     list.add("Jason");     list.add("Jack");     list.add(222);     //创建一个迭代器     Iterator it = list.iterator();     while (it.hasNext()) {       //将集合中的内容强制转换       String o =(String) it.next();       //打印出他们各自的长度       System.out.println(o.length());     }   } }

控制台输出:

5 4 Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String     at GenericityTest01.main(GenericityTest01.java:17)

2、使用泛型的情况

就可以在编译时进行语法检查 ,避免了在运行时出现ClassCastException异常,同时也避免了类型的强制类型转换。   /** * @author Jason * @create 2020-07-11 11:47 * 泛型实例 */ public class GenericityTest04 {   public static void main(String[] args) {     Collection<String> list = new ArrayList<String>();     list.add("Jason");     list.add("Jack");     //list.add(222);     //创建一个迭代器     Iterator it = list.iterator();     while (it.hasNext()) {       //将集合中的内容强制转换       String o = (String) it.next();       //打印出他们各自的长度       System.out.println(o.length());     }   } }

控制台输出:

5 4

3、新的问题

刚我们存入的是String,而如果我们需要存入Integer,则还要单独编写一个ArrayList,实际上还需要为其他所有class单独编写一种ArrayList,需要解决这样的问题,我们只需要将ArrayList编写一种模板。实现我们想要什么都可以。 /** * @author Jason * @create 2020-07-11 14:49 * */ public class MyClass {   public static void main(String[] args) {     GenericityTest05<String> strgeneric = new GenericityTest05<>("jason");     String key1 = strgeneric.getKey();     System.out.println("key1:" + key1);     System.out.println("----------");     GenericityTest05<Integer> intgeneric = new GenericityTest05<>(111);     Integer key2 = intgeneric.getKey();     System.out.println("key2:" + key2);   } } ** * @author Jason * @create 2020-07-11 14:42 */ public class GenericityTest05<T> {   private T key;   public GenericityTest05() {   }   public GenericityTest05(T key) {     this.key = key;   }   public T getKey() {     return key;   }   public void setKey(T key) {     this.key = key;   }   @Override   public String toString() {     return "GenericityTest05{" +             "key=" + key +             '}';   } }

控制台输出:

key1:jason ---------- key2:111

二、泛型常用类型:

E - Element (在集合中使用,因为集合中存放的是元素) T - Type(表示Java 类,包括基本的类和我们自定义的类) K - Key(表示键,比如Map中的key) V - Value(表示值) N - Number(表示数值类型) ? - (表示不确定的java类型) S、U、V - 2nd、3rd、4th types  

三、泛型类

泛型类:是在实例化类的时候指明泛型的具体类型 (1)使用语法 类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>(); (2)Java1.7以后,后面的<>中的具体的数据类型可以省略不写 类名<具体的数据类型> 对象名 = new 类名<>(); 菱形语法 (3)注意事项 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object 泛型的类型参数只能是类类型,不能是基本数据类型 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型 (4)泛型类实例: /** * @author Jason * @create 2020-07-11 15:12 * 年会抽奖器 */ public class ProductGetter<T> {   Random random = new Random();   //奖品   private T product;   //奖品池   ArrayList<T> list = new ArrayList<>();   //添加奖品   public void addProduct(T t) {     list.add(t);   }   //抽奖   public T getProduct(){     product = list.get(random.nextInt(list.size()));     return product;   }   public static void main(String[] args) {     //创建抽奖器对象     ProductGetter<String> strProduct = new ProductGetter<>();     String[] strProducts = {"Apple", "HuaWei", "XiaoMi", "SanXing"};     //给抽奖器中添加奖品     for (int i = 0; i < strProducts.length; i++) {       strProduct.addProduct(strProducts[i]);     }     //抽奖     String product1 = strProduct.getProduct();     System.out.println("恭喜您抽中了:"+product1);     System.out.println("---------------");     ProductGetter<Integer> intProduct = new ProductGetter<>();     int[] intProducts = {1000, 2000, 3000, 5000};     for (int i = 0; i < intProducts.length; i++) {       intProduct.addProduct(intProducts[i]);     }     Integer product2 = intProduct.getProduct();     System.out.println("恭喜您抽中了:"+product2);   } }

(5)从泛型类派生子类

    • 子类也是泛型类,子类和父类的泛型类型要一致         class ChildGeneric<T> extends Generic<T> /** * @author Jason * @create 2020-07-11 15:36 */ public class Parent<E> {   private E value;   public E getValue() {     return value;   }   public void setValue(E value) {     this.value = value;   } } /** * @author Jason * @create 2020-07-11 15:39 * 子类也是泛型类 */ public class ChildFirst<T> extends Parent<T> {   @Override   public T getValue() {     return super.getValue();   } } /** * @author Jason * @create 2020-07-11 15:41 */ public class MyClass {   public static void main(String[] args) {     ChildFirst<String> childFirst = new ChildFirst<String>();     childFirst.setValue("Jason");     String value = childFirst.getValue();     System.out.println(value);   } }

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

 class ChildGeneric extends Generic<String>

/** * @author Jason * @create 2020-07-11 15:36 */ public class Parent<E> {   private E value;   public E getValue() {     return value;   }   public void setValue(E value) {     this.value = value;   } } /** * @author Jason * @create 2020-07-11 15:48 * 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型(创建子类的时候无法确定父类类型) */ public class ChildSecond extends Parent<Integer> {   @Override   public Integer getValue() {     return super.getValue();   }   @Override   public void setValue(Integer value) {     super.setValue(value);   } } /** * @author Jason * @create 2020-07-11 15:41 */ public class MyClass {   public static void main(String[] args) {     ChildSecond childSecond = new ChildSecond();     childSecond.setValue(100);     Integer value1 = childSecond.getValue();     System.out.println(value1);   } }

四、泛型接口

1、泛型接口的定义语法: interface 接口名称 <泛型标识,泛型标识,…> {     泛型标识 方法名();     ..... }

2、泛型接口详情

实现类不是泛型类,接口要明确数据类型 /** * @author Jason * @create 2020-07-11 16:08 * 泛型接口 */ public interface Generator<T> {   T getKey(); } /** * @author Jason * @create 2020-07-11 16:11 * 实现泛型接口的类不是泛型类,需要明确实现泛型接口的数据类型 */ public class Apple implements Generator<String> {   @Override   public String getKey() {     return "Hello Jason!";   } } /** * @author Jason * @create 2020-07-11 16:13 */ public class Test {   public static void main(String[] args) {     Apple apple = new Apple();     String key = apple.getKey();     System.out.println(key);   } } 实现类也是泛型类,实现类和接口的泛型类型要一致 /** * @author Jason * @create 2020-07-11 16:08 * 泛型接口 */ public interface Generator<T> {   T getKey(); } /** * @author Jason * @create 2020-07-11 16:17 * 泛型类实现泛型接口,需要保证实现接口的泛型类的泛型标识包含泛型接口的泛型标识 */ public class Pair<T,E> implements Generator<T> {   private T key;   private E value;   public Pair(T key, E value) {     this.key = key;     this.value = value;   }   @Override   public T getKey() {     return key;   }   public E getValue() {     return value;   } } /** * @author Jason * @create 2020-07-11 16:13 */ public class Test {   public static void main(String[] args) {     Pair<String,Integer> pair = new Pair<>("count",100);     String key1 = pair.getKey();     Integer value = pair.getValue();     System.out.println(key1 + " = " + value);   } }

五、泛型方法

1、概述 泛型方法:是在调用方法的时候指明泛型的具体类型 2、语法: 修饰符 <T,E, ...> 返回值类型 方法名(形参列表) { 方法体... } 3、说明: public与返回值中间非常重要,可以理解为声明此方法为泛型方法。 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 4、实例 /** * @author Jason * @create 2020-07-11 15:12 * 年会抽奖器 */ public class ProductGetter<T> {   Random random = new Random();   //奖品   private T product;   //奖品池   ArrayList<T> list = new ArrayList<>();   //添加奖品   public void addProduct(T t) {     list.add(t);   }    //抽奖方法    public T getProduct(){      product = list.get(random.nextInt(list.size()));      return product;    }   //定义泛型方法   public <E> E getProduct(ArrayList<E> list) {     return list.get(random.nextInt(list.size()));   }    //静态方法方法,采用多个泛型类型    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());    }     //泛型可变参数的定义     public static <E> void print(E... e) {       for (int i = 0; i < e.length; i++) {         System.out.println(e[i]);       }     }   public static void main(String[] args) {     ProductGetter<Integer> productGetter = new ProductGetter<>();     ProductGetter<Integer> intProduct = new ProductGetter<>();     int[] intProducts = {1000, 2000, 3000, 5000};     for (int i = 0; i < intProducts.length; i++) {       intProduct.addProduct(intProducts[i]);     }     //泛型类的成员方法的调用     Integer product2 = intProduct.getProduct();     System.out.println("恭喜您抽中了:"+product2);     System.out.println(product2+"\t"+product2.getClass().getSimpleName());     System.out.println("+++++++++++++++++");     ArrayList<String> strArrayList = new ArrayList<>();     strArrayList.add("笔记本电脑");     strArrayList.add("苹果电脑");     strArrayList.add("华为手机");     strArrayList.add("ipad");     //泛型方法的调用,类型通过调用方法的时候来指定     String product = productGetter.getProduct(strArrayList);     System.out.println(product+"\t"+product.getClass().getSimpleName());      System.out.println("+++++++++++++++++");     ArrayList<Integer> intArrayList = new ArrayList<>();     intArrayList.add(1000);     intArrayList.add(2000);     intArrayList.add(3000);     intArrayList.add(5000);     Integer product3 = productGetter.getProduct(intArrayList);     System.out.println(product3+"\t"+product3.getClass().getSimpleName());     System.out.println("----------------");     //调用多个泛型类型的静态泛型方法     productGetter.printType(100,"jason",true);     System.out.println("----------------");     //调用可变参数     productGetter.print(1, 2, 3, 4);     System.out.println("----------------");     productGetter.print("a","b","c");   } }

5、总结

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

 六、类型通配符

1、什么是类型通配符? 类型通配符一般是使用"?"代替具体的类型实参。 所以,类型通配符是类型实参,而不是类型形参。 /** * @author Jason * @create 2020-07-12 8:05 */ public class Box<E> {   private E first;   public E getFirst() {     return first;   }   public void setFirst(E first) {     this.first = first;   } } /** * @author Jason * @create 2020-07-12 8:06 * 通配符 */ public class Test04 {   public static void main(String[] args) {     Box<Number> box = new Box<>();     box.setFirst(100);     showBox(box);     //如果此时我们想传一个Integer类型的肯定会报错     //即使showBox方法是Number类型或者Integer类型都是不可以的     /*Box<Integer> box1 = new Box<>();     box1.setFirst(200);     showBox(box1);*/     //如果此时我们在showBox上使用通配符会怎么样呢?当然问题就解决了     Box<Integer> box1 = new Box<>();     box1.setFirst(200);     showBox(box1);   }   /*public static void showBox(Box<Number> box){     Number first = box.getFirst();     System.out.println(first);   }*/   //这里虽然是换了通配符,但是还是会存在一个问题,需要用Object类型接收   public static void showBox(Box<?> box){     Object first = box.getFirst();     System.out.println(first);   } }

2、通配符上限

语法: 类/接口<? extends 实参类型> 要求该泛型的类型,只能是实参类型,或实参类型的子类类型 public class Animal { } public class Cat extends Animal { } public class MiniCat extends Cat { } /** * @author Jason * @create 2020-07-12 8:33 * 通配符上限 */ public class TestUp {   public static void main(String[] args) {     ArrayList<Animal> animals = new ArrayList<>();     ArrayList<Cat> cats = new ArrayList<>();     ArrayList<MiniCat> miniCats = new ArrayList<>();     //这里为啥可以添加呢?原因在于他的底层源码:public boolean addAll(Collection<? extends E> c)     cats.addAll(cats);     cats.addAll(miniCats);     //这里会报异常:原因是什么呢?问题在于showAnimal()采用的是通配符上限     //showAnimal(animals);     showAnimal(cats);     showAnimal(miniCats);   }   //泛型通配符上限,传递的集合类型,只能是Cat或Cat的子类   public static void showAnimal(ArrayList<? extends Cat> list) {     //这里是不可以添加元素的,因为ArrayList是上限通配符,此时我们是无法知道他存的是何种类型的元素     /*list.add(new Animal());     list.add(new Cat());*/     for (int i = 0; i < list.size(); i++) {       Cat cat = list.get(i);     }   } }

3、通配符下限

语法: 类/接口<? super 实参类型> 要求该泛型的类型,只能是实参类型,或实参类型的父类类型。 实例一: /** * @author Jason * @create 2020-07-12 8:57 * 通配符下限 */ public class TestDown {   public static void main(String[] args) {     ArrayList<Animal> animals = new ArrayList<>();     ArrayList<Cat> cats = new ArrayList<>();     ArrayList<MiniCat> miniCats = new ArrayList<>();     //集合只能是Cat或Cat的父类类型,不能是它的子类型     showAnimal(animals);     showAnimal(cats);     //showAnimal(miniCats);   }   //类型通配符下限,要求集合只能是Cat或Cat的父类类型   public static void showAnimal(List<? super Cat> list) {     //这里可以添加元素,并且他本身和子类都能添加,for循环是object类型接收的     list.add(new Cat());     list.add(new MiniCat());     //list.add(new Animal());     for (Object o : list) {       System.out.println(o);     }   } }

实例二:

/** * @author Jason * @create 2020-07-12 9:23 * 下限通配符 */ public class Test05 {   public static void main(String[] args) {     //这里的TreeSet底层采用的是下限通配符:public TreeSet(Comparator<? super E> comparator)     //我们在构造子类对象的时候,必须先构造父类对象,如果这时候比较的是父类成员的话是没有任何问题的。     //而如果这里比较子类对象的话,则不可以,因为它还没有构造出来     //TreeSet<Cat> treeSet = new TreeSet<>(new Comparator2());     TreeSet<Cat> treeSet = new TreeSet<>(new Comparator1());     treeSet.add(new Cat("jack", 10));     treeSet.add(new Cat("mimi", 14));     treeSet.add(new Cat("jim", 11));     treeSet.add(new Cat("bibi", 16));     treeSet.add(new Cat("didi", 6));     for (Cat cat : treeSet) {       System.out.println(cat);     }   } } class Comparator1 implements java.util.Comparator<Animal> {   @Override   public int compare(Animal o1, Animal o2) {     return o1.name.compareTo(o2.name);   } } class Comparator2 implements Comparator<Cat> {   @Override   public int compare(Cat o1, Cat o2) {     return o1.age-o2.age;   } } class Comparator3 implements Comparator<MiniCat> {   @Override   public int compare(MiniCat o1, MiniCat o2) {     return o1.level-o2.level;   } }

七、泛型擦除

1、概述 泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。   2、实例 /** * @author Jason * @create 2020-07-12 10:00 */ public class Test06 {   public static void main(String[] args) {     ArrayList<Integer> intList = new ArrayList<>();     ArrayList<String> strList = new ArrayList<>();     System.out.println(intList.getClass().getSimpleName());     System.out.println(strList.getClass().getSimpleName());     System.out.println("=============");     System.out.println(intList.getClass() == strList.getClass());   } }

控制台输出:

ArrayList ArrayList ============= true

3、无限制类型擦除

擦除前 /** * @author Jason * @create 2020-07-12 10:05 */ public class Erasure<T> {   private T key;   public T getKey() {     return key;   }   public void setKey(T key) {     this.key = key;   } } 擦除后 /** * @author Jason * @create 2020-07-12 10:05 */ public class Erasure {   private Object key;   public Object getKey() {     return key;   }   public void setKey(Object key) {     this.key = key;   } }

4、有限制类型擦除

擦除前 /** * @author Jason * @create 2020-07-12 10:05 */ public class Erasure<T extends Number> {   private T key;   public T getKey() {     return key;   }   public void setKey(T key) {     this.key = key;   } } 擦除后 /** * @author Jason * @create 2020-07-12 10:05 */ public class Erasure {   private Number key;   public Number getKey() {     return key;   }   public void setKey(Number key) {     this.key = key;   } }

5、擦除方法中定义的参数

擦除前 public <T extends Number> T getValue(T value) {   return t; } 擦除后 public Number getValue(Number value) {   return value; }

6、桥接方法

擦除前 /** * @author Jason * @create 2020-07-12 10:31 * 泛型接口 */ public interface Info<T> {   T info(T t); } public class InfoImpl implements Info<Integer> {   @Override   public Integer info(Integer integer) {     return null;   } } 擦除后 /** * @author Jason * @create 2020-07-12 10:31 * 泛型接口 */ public interface Info<T> {   Object info(Object var); } public class InfoImpl implements Info {   public Integer info(Integer var) {     return null;   } @Override   public Object info(Object var) {     return info((Integer) var);   } }

八、泛型与数组

可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象 /** * @author Jason * @create 2020-07-12 10:53 * 泛型与数组 */ public class demo07 {   public static void main(String[] args) {     //不能直接创建带泛型的数组对象     //ArrayList<String>[] listArr = new ArrayList<>();     ArrayList<String>[] listArr = new ArrayList[5];     ArrayList<Integer> intList = new ArrayList<>();     intList.add(100);     ArrayList<String> strList = new ArrayList<>();     strList.add("jason");     listArr[0] = strList;     String s = listArr[0].get(0);     System.out.println(s);   } }

 

可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组 /** * @author Jason * @create 2020-07-12 11:07 */ public class Fruit<T> {   private T[] array;   public Fruit(Class<T> clz, int length) {     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;   } } /** * @author Jason * @create 2020-07-12 10:53 * 泛型与数组 */ public class Test07 {   public static void main(String[] args) {     Fruit<String> fruit = new Fruit<>(String.class, 3);     fruit.put(0, "桃子");     fruit.put(1, "栗子");     fruit.put(2, "苹果");     System.out.println(Arrays.toString(fruit.getArray()));     String s1 = fruit.get(2);     System.out.println(s1);   } }

九、泛型和反射

1、常用泛型类: Class<T> Constructor<T> 2、实例(分为使用泛型和不使用泛型) /** * @author Jason * @create 2020-07-12 11:19 */ public class Person {   private String name;   public String getName() {     return name;   }   public void setName(String name) {     this.name = name;   } } /** * @author Jason * @create 2020-07-12 11:20 */ public class Test08 {   public static void main(String[] args) throws Exception {     //使用泛型     Class<Person> personClass = Person.class;     Constructor<Person> constructor = personClass.getConstructor();     Person person = constructor.newInstance();     //不使用泛型创建对象的时候是object类型的,后续还需要进行数据类型的转化     Class personClass1 =Person.class;     Constructor constructor1 = personClass1.getConstructor();     Object o = constructor1.newInstance();   } }

 

                   
最新回复(0)