泛型

一、为什么要有泛型

1、泛型的设计背景:

集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的不放是确定的,李儒关羽这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。

2、为什么要有泛型?

那么为什么要有泛型呢,直接Object不是也可以存储数据吗?

1、解决元素存储的安全性问题,好比商品、药品标签,不会弄错。2.解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别。

 2、Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

 

二、泛型的使用

1.泛型是jdk 5.0新增的特性 

 2、在集合中使用泛型:

总结:

①集合接口或集合类在jdk5.0时都修改为带泛型的结构。

②在实例化集合类时,可以指明具体的泛型类型

③指明完以后,在集合或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。

  比如:add(E e) --->实例化以后:add(Integer e)

④注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换。

⑤如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型

注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换

三、自定义泛型类

public class OrderTest<T>{

    Map<Integer,T> map;
    T type;

    public T getType() {
        return type;
    }

    public void setType(T type) {
        this.type = type;
    }

    public void setType(Integer id, T entity) {
        map.put(id,entity);
    }

    public T getType(Integer id) {
        return map.get(id);
    }

    public void update(Integer id,T entity){
        if (map.containsKey(id)){
            map.put(id,entity);
        }
    }
    public List<T> list(){
        Collection<T> values = map.values();

        return new ArrayList<>(values);
    }
    public T delete(Integer id){
        return map.remove(id);
    }

    public <E> List<E> test(E[] arr){
        List<E> list = new ArrayList<>();
        Collections.addAll(list, arr);
        return list;
    }
}

自定义泛型方法:

    public <E> List<E> test(E[] arr){
        List<E> list = new ArrayList<>();
        Collections.addAll(list, arr);
        return list;
    }

注意点:

1、泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>

2、泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass(){}

3、实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

4、泛型不同的引用不能相互赋值。尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。

5、泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
6、经验:泛型要使用一路都用。要不用,一路都不要用。

7、如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

8、jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();

9、泛型的指定中不能使用基本数据类型,可以使用包装类替换。

10、在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

11、异常类不能是泛型的

四、泛型在继承上的体现

不能使用new E[]。但是可以: E[] elements = (E[])new Object[capacity];

参者: ArrayList源码中声 明: Object] elementData,而非泛型参数类型数组。

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

  • 子类不保留父类的泛型:按需实现
    •   没有类型擦除
    •   具体类型
  • 子类保留父类的泛型:泛型子类
    •   全部保留
    •   部分保留

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

    public void test(){
        Object obj = new Object();
        String str = new String();
        obj = str;

        Object[] objects = null;
        String[] strings = null;
        objects = strings;
        //编译不通过
        //Date date = new Date();
        //str = date;
        //不兼容的类型。实际为 java.util.Date',需要 'java.lang.String'
        List<Object> list1 = null;
        List<String> list2 = null;
        //此时的list1和list2的类型不具有字符类关系
        //编译不通过
        //list1 = list2;
        /*
            反证法:
                假设list1 = list2;
                list1.add(123);导致混入非String的数据。出错
         */
        show(list1);
        show1(list2);
    }
    public void show1(List<String> list){

    }
    public void show(List<Object> list){

    }

虽然类A是类B的父类,但是G<A>和G<B>二者不具备字符类关系,二者是并列关系。

补充:类A是类B的父类,A<G>是B<G>的父类

五、通配符的使用

使用类型

1、通配符:?

  比如:List<?> ,Map<?,?>
  List<?>是List、List等各种泛型List的父类。
2、读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。

3、写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。

  1. 唯一的例外是null,它是所有类型的成员。
  2. 将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList(); 

c.add(new Object()); // 编译时错误因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。

4、另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。

代码演示:

/**
 * 1.泛型在继承方面的体现
 * 2.通配符的使用
 */
public class GenericTest {

    /**
     * 2.通配符的使用
     * 通配符:?
     *
     * 类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
     */
    @Test
    public void test3(){
        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 obj = iterator.next();
            System.out.println(obj);
        }
    }
}

 

 

posted @ 2022-07-28 16:06  羽十六  阅读(30)  评论(0)    收藏  举报