一、泛型简介

1. 泛型的使用

  • JDK5.0新增
  • 在定义类/接口时通过泛型指定类中某个属性的类型或某个方法返回值及参数类型
  • 泛型的类型必须是类,不能是基本数据类型,泛型默认类型为java.lang.Object类型

2. 在集合中使用泛型

  • 集合接口/集合类在JDK5.0起都修改为带泛型的结构,实例化时可指明具体的泛型类型
  • 凡是声明了泛型的内部结构,都应定义为泛型类型 例如: add(E e) 实例化后: add(Integer e)

二、自定义泛型结构

1. 泛型类、泛型接口

public class Order<T> {
    // 类的内部结构可以使用类的泛型
    T orderT;

    public Order(T orderT) {
        this.orderT = orderT;
    }

    public Order() {}

    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }
}
Order order1 = new Order();
    order1.setOrderT(123);
//  order1.setOrderT("abc"); // 错误

    Order<Integer> order2 = new Order<>(123);
    order2.setOrderT(456);
  • 泛型类有多个参数,应将多个参数放在<>内,eg. <E1, E2, E3>
  • 泛型类的构造器: public GenericClass(){}   错误写法: public GenericClass<E>(){} 
  • 泛型不同的引用不能i相互赋值
  • 如果泛型结构是接口/抽象类,则不可创建泛型类的对象
  • JDK7泛型的简化操作: ArrayList<Generic> list= new ArrayList<>; 
  • 在静态方法中不能使用类的泛型  错误写法: public static void show(T OrderT) {} 
  • 异常类不能声明为泛型类 错误写法: public class MyException<T> extends Exception{}
  • 不能使用new E[], 但可以  E[] elements = (E[]) new Object[capacity]; 
//  T[] arr = new T[10]; // 编译不通过
    T[] arr = (T[]) new Object[10]; // 正确写法
  • 父类的泛型,子类可以选择保留泛型,可以选择指定泛型类型
class Father<T1, T2> {}

// 子类不保留父类的泛型
// 1) 没有类型 擦除
class Sub1 extends Father {} // 等价于class Sub1 extends Father<Object, Object>{}
// 2) 具体类型
class Sub2 extends Father<Integer, Integer> {}

// 子类保留父类的泛型
// 1) 全部保留
class Sub3<T1, T2> extends Father<T1, T2> {}
// 2) 部分保留
class Sub4<T2> extends Father<Integer, T2> {}

2. 泛型方法

  •  访问权限  <泛型>  返回值类型  方法名  (泛型标记 参数名称, ...)  异常  { ... } 
  • 方法中出现泛型结构,泛型参数与类的泛型参数没有任何关系,所属类是不是泛型类都可以
  • 泛型方法可以声明为静态的,因为泛型参数是在调用方法时确定的,并非实例化时确定
public <E> List<E> copyFromArrayToList(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e : arr) {
            list.add(e);
        }
        return list;
    }
  • 泛型方法在调用时指明参数类型,与类的泛型类型没有任何关系
Order<String> order = new Order<>();
    Integer[] arr = new Integer[]{1, 2, 3, 4};
    List<Integer> list = order.copyFromArrayToList(arr);
  • 应用举例
// DAO: Database Access Object
// 表的共性操作的DAO
public class DAO<T> {
    // 添加记录
    public void add(T t) {}

    // 删除记录
    public boolean remove(int index) { return false; }

    // 修改记录
    public void update(int index, T t) {}

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

    // 查询多条记录
    public List<T> getForList(int index) { return null; }

    // 泛型方法:获取表中一共有多少条记录(Long型) / 获取最大的员工入职时间(Date)
    public <E> E getValue(){
        return null;
    }
}

// 此类对应数据库中Customer表,一个Customer类的对象对应表中的一条数据
class Customer {}

// 专门操作数据库中一张Customer表
class CustomerDAO extends DAO<Customer> {}

// 此类对应数据库中Student表,一个Student类的对象对应表中的一条数据
class Student {}

// 只能操作一个表的DAO
class StudentDAO extends DAO<Student> {}

class DAOTest {
    @Test public void test() {
        CustomerDAO dao1 = new CustomerDAO();
        dao1.add(new Customer());
        List<Customer> list1 = dao1.getForList(10);

        StudentDAO dao2 = new StudentDAO();
        dao2.add(new Student());
        List<Student> list2 = dao2.getForList(10);
    }
}

三、泛型在继承上的体现

  • 类A是类B的父类, G<A> G<B> 不具备子父类关系,二者是并列关系
Date date = new Date();
//  str = date; // 编译不通过
    
    List<Object> list1 = null;
    List<String> list2 = null;
//  list1 = list2; // 编译不通过,此时list1和list2不具有子父类关系 

    // 反证法证明G<A>和G<B>不具有子父类关系
    list2 = new ArrayList<>();
//  list1 = list2; // 假设可以,listObj指向listStr的堆空间
//  list1.add(123); // 导致listStr中混入非String类型数据
  • 类A是类B的父类/接口, A<G> B<G> 的父类
AbstractList<String> list1 = null;
    List<String> list2 = null;
    ArrayList<String> list3 = null;
    list1 = list3;
    list2 = list3;

四、通配符的使用

1)通配符

  • 通配符:?
  • 类A是类B的父类, G<A> G<B> 没有关系,二者共同的父类是 G<?> 
@Test public void test3() {
        List<Object> list1 = null;
        List<Object> 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);
        }
    }
  • 写入:对于 List<?> 不能向其中添加数据,除了添加null外
  • 读取:对于List<?> 允许读取其中数据,数据类型为Object
List<String> list3 = new ArrayList<>();
    list3.add("aa");
    list3.add("bb");

    List<?> list = null;
    list = list3;

//  list.add("cc"); // 编译错误
    list.add(null);

    Object obj = list.get(0); // aa

2)有限制条件的通配符

  •  ? extends A G<? extends A>  可以作为 G<A> G<B> 的父类,其中B是A的子类
  •  ? super A G<? super A>  可以作为 G<A> G<B> 的父类,其中B是A的父类
List<? extends Person> list1 = null;
    List<? super Person> list2 = null;

    List<Student> list3 = null;
    List<Person> list4 = null;
    List<Object> list5 = null;

    list1 = list3;
    list1 = list4;
//  list1 = list5; // 编译错误

//  list2 = list3; // 编译错误
    list2 = list4;
    list2 = list5;
  • 数据读取
list1 = list3;
    Person person = list1.get(0);
//  Student stu = list1.get(0); // 编译错误,不能写Student

    list1 = list4;
    Person person1 = list1.get(0); // 不能写Student

    list2 = list4;
    Object obj = list2.get(0); // 只能写Object,不能写Person
  • 数据写入
List<? extends Person> list1 = null;
//  list1.add(new Student()); // 编译错误
//  list1.add(new Person()); // 编译错误

    List<? super Person> list2 = null;
//  list2.add(new Object()); // 编译错误,只能放Person和Person的子类
    list2.add(new Person());
    list2.add(new Student());

六、泛型的应用举例

  • 举例1
HashMap<String, ArrayList<Citizen>> map = new HashMap<>();
    ArrayList<Citizen> list = new ArrayList<>();
    list.add(new Citizen("Alice"));
    list.add(new Citizen("Bob"));
    list.add(new Citizen("Cindy"));
    map.put("Alice", list);

    Set<Entry<String, ArrayList<Citizen>>> entrySet = map.entrySet();
    Iterator<Entry<String, ArrayList<Citizen>>> iterator = entrySet.iterator();
    while (iterator.hasNext()) {
        Entry<String, ArrayList<Citizen>> entry = iterator.next();
        String key = entry.getKey();
        ArrayList<Citizen> value = entry.getValue();
        System.out.println("户主:" + key);
        System.out.println("家庭成员" + value);
    }
  • 举例2
public class DAO<T> {
    private Map<String, T> map = new HashMap<>();

    public void save(String id, T entity) {
        map.put(id, entity);
    }

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

    public void update(String id, T entity) {
        if (map.containsKey(id)) { // 判断键值均存在,再进行修改
            map.put(id, entity);
        }
    }

    public List<T> list() {
//      return (List<T>) map.values(); // 错误;values无序不可重复

        ArrayList<T> list = new ArrayList<>();
        Collection<T> values = map.values();
        for (T t : values) {
            list.add(t);
        }
        return list;
    }

    public void delete(String id) {
        map.remove(id);
    }
}
public static void main(String[] args) {
        DAO<User> dao = new DAO<>();
        dao.save("1001", new User("Alice", 18));
        dao.save("1002", new User("Bob", 20));
        dao.save("1003", new User("Cindy", 22));

        List<User> list = dao.list();
        list.forEach(System.out::println); // 使用forEach打印list

        dao.update("1001", new User("Cathy", 22));

        User user = dao.get("1001"); // Cathy,22

        dao.delete("1002");
    }