(二) 自定义泛型结构

1. 泛型的声明

interface List<T> 和 class GenTest<K,V>
其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。
常用T表示,是Type的缩写

2. 泛型的实例化

一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
  • T只能是类,不能用基本数据类型填充。但可以使用包装类填充
  • 把一个集合中的内容限制为一个特定的数据类型,这就是generics背后
的核心思想

3. 自定义泛型结构:泛型类、泛型接口注意的地方

1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
<E1,E2,E3>
2. 泛型类的构造器如下:public GenericClass(){}。
而下面是错误的:public GenericClass<E>(){}
3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4. 泛型不同的引用不能相互赋值。
>尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有
一个ArrayList被加载到JVM中。
5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价
于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
7. jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
 	@Test
    public void test(){
        // 使用时:类似于Object,不等同于Object
        ArrayList list = new ArrayList();
       // list.add(new Date());  有风险

        list.add("hello");

        // 泛型擦除,编译时不会类型检查
        testArray(list);

        // ArrayList<Object> list2 = new ArrayList<Object>();

        // testArray(list2);   一旦指定了Object,编译时就会检查,必须按照Object来处理

    }
    public static void testArray(ArrayList<String> list) {
        String str = "";
        for (String s :list) {
            str += s + ",";
        }
        System.out.println("元素:" + str);
    }

9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
10. 异常类不能是泛型的
11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
  • 子类不保留父类的泛型:按需实现
    • 没有类型 擦除  
    • 具体类型
  • 子类保留父类的泛型:泛型子类
    • 全部保留
    • 部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
public class Father<T1,T2> {}

/**
 * 子类不保留父类的泛型
 * 1) 没有类型擦除
 */
public class Son1 extends Father {
    /**
     * 等价于   public class Son1 extends Father<Object,Object> {}
     */
}

/**
 * 2)具体类型
 */
public class Son2 extends Father<Integer,String> {}

/**
 * 子类保留父类的泛型
 * 1) 全部保留
 */
public class Son3<T1,T2> extends Father<T1,T2> {}

/**
 * 2)部分保留
 */
public class Son4<T2> extends Father<Integer,String>{}
public class Father<T1,T2> {}

/**
 * 子类不保留父类的泛型
 * 1) 没有类型擦除
 */
public class Son1<A,B> extends Father {
    /**
     * 等价于   public class Son1 extends Father<Object,Object> {}
     */
}

/**
 * 2)具体类型
 */
public class Son2<A,B> extends Father<Integer,String> {}

/**
 * 子类保留父类的泛型
 * 1) 全部保留
 */
public class Son3<T1,T2,A,B> extends Father<T1,T2> {}

/**
 * 2)部分保留
 */
public class Son4<T2,A,B> extends Father<Integer,String>{}

4. 自定义泛型

package com.liufei.test.myselfTest;
import java.util.ArrayList;
import java.util.List;
public class Order<T> {
    
    String orderName;
    int orderId;
    /**
     * 1. 类的内部构造就可以使用类的泛型
     */
    T orderT;

    public Order(){
        // 编译不通过
       // T[] ts = new T[10];

        // 编译通过
        T[] arr = (T[]) new Object[10];
    }

    public Order(String orderName,int orderId,T orderT) {
        this.orderId = orderId;
        this.orderName = orderName;
        this.orderT = orderT;
    }

    // 如下的三个方法都不是泛型方法
    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }

    /**
     *  2. 静态方法中不能使用泛型。
     */

   /* public static void show(T orderT) {
        System.out.println(orderT);
    }*/

    /**
     * 3. 异常中不能使用泛型
     */
    public void show(){
        // 编译就不会通过
       /* try {

        } catch (T t) {

        }*/
    }

    /**
     * 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
     * 换句话说,泛型方法所属的类是不是泛型类都没有关系。
     * 4. 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
     */
    public static <E> List<E> copyFromArrayToList(E[] arr) {
        ArrayList<E> list = new ArrayList<E>();

        if (arr != null) {
            for (E e : arr) {
                list.add(e);
            }
        }
        return list;
    }
}
package com.liufei.test.myselfTest;
import java.util.ArrayList;
import java.util.List;

public class SubOrder extends Order<Integer> { // SubOrder 不是泛型

    public static <E> List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        if (arr != null) {
            for(E e : arr){
                list.add(e);
            }
        }
        return list;
    }
}
package com.liufei.test.myselfTest;
public class SubOrder1<T> extends Order<T> {
    /**
     * SubOrder1<T>:仍然是泛型类
     */
}

5. 泛型实例

package com.liufei.test.java;
import java.util.List;
public class DAO<T> {
    /**
     * 添加记录
     * @param t
     */
    public void add(T t) {

    }
    /**
     * 删除记录
     * @param t
     * @return
     */
    public boolean remove(T t) {
        return false;
    }

    /**
     * 修改记录
     */
    public boolean update(T t) {
        return false;
    }

    /**
     * 查询一条记录
     */
    public T getIndex(int index) {
        return null;
    }

    /**
     * 查询多条记录
     * @param index
     * @return
     */
    public List<T> getForList(int index) {
        return null;
    }
    // 泛型方法
    // 举例:获取表中一共有多少条记录?获取最大的员工入职时间
    public <E> E getValue(){
        return null;
    }
}
public class StudentDAO extends DAO<Student> {}

6. 泛型在继承上的使用

package com.liufei.test.java1;

import org.junit.Test;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
/**
 * 1. 泛型在继承方面的使用
 */
public class GenericTest {
    /**
     * 1.泛型在继承方面的体现
     *  虽然类A是类B的父类,但是G<A> 和 G<B> 二者不具备 子父类关系,二者是并列关系。
     *  补充:类A是类B的父类,A<G> 是 B<G> 的父类
     */
    @Test
    public void test1(){
        Object obj = null;
        String str = null;
        obj = str;

        Object[] arr1 = null;
        Object[] arr2 = null;
        arr1 = arr2;

        // 编译不通过
       /* Date date = new Date();
        str = obj;*/

        List<Object> list1 = null;
        List<String> list2 = new ArrayList<>();
        // 此时的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){

    }

    @Test
    public void test2(){
        AbstractList<String> list1 = null;
        List<String> list2 = null;
        ArrayList<String> list3 = null;

        list1 = list3;
        list2 = list3;

    }
    /**
     * 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;

        List<String> list3 = new ArrayList<>();
        list3.add("AA");
        list3.add("BB");
        list3.add("CC");
        list = list3;

        // 添加(写入):对于List<?> 就不能向其内部添加数据了
        // 除了添加 null 之外
       // list.add("DDD");   // 编译不通过
        list.add(null);

        // 允许读取数据。读取的数据为Object
        Object o = list.get(0);
        System.out.println(o);
    }

    /**
     *3.有限制条件的通配符的使用。
     *  ? extends A:
     *      G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
     *
     *  ? super A:
     *      G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
     */
    @Test
    public void test4(){
        List<? extends Person> list1 = new ArrayList<>();
        List<? super Person> list2 = new ArrayList<>();

        List<Student> list3 = new ArrayList<>();
        List<Person> list4 = new ArrayList<>();
        List<Object> list5 = new ArrayList<>();

        list1 = list3;
        list1 = list4;

        //list1 = list5;  // 编译不通过
        // list2 = list3; // 编译不通过

        list2 = list4;
        list2 = list5;

        // 读取数据
        Person person = list1.get(0);
        // 编译不通过
        // Student s = list1.get(0);

        Object object = list2.get(0);
        // 编译不通过
        // Person p1 = list2.get(0);

        // 写入数据
        list2.add(new Person());
        list2.add(new Student());
    }
}
package com.liufei.test.java1;
public class Person {
}
package com.liufei.test.java1;
public class Student extends Person {
}

7.通配符

1.使用类型通配符:?
        比如:List<?> ,Map<?,?>
        List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
  •   唯一的例外是null,它是所有类型的成员。
  • 将任意元素加入到其中不是类型安全的:
    Collection<?> c = new ArrayList<String>();
    c.add(new Object()); // 编译时错误
    因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去
  • 唯一的例外的是null,它是所有类型的成员。 
  • 另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object

通配符的使用

    // 注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<> 不能使用?
    public static <?> void test(ArrayList<?> list) {
    }

    // 注意点2:编译错误:不能用在泛型类声明上
    class GenericTypeClass<?> {
    }

    // 注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
    @Test
    public void test5(){
        // 编译错误
        ArrayList<?> list = new ArrayList<?>();
        // 编译通过
        ArrayList<?> list2 = new ArrayList<>();
    }




posted @ 2021-08-08 22:34  刘翊扬  阅读(74)  评论(0编辑  收藏  举报