目录

一、为什么要有泛型

二、在集合中使用泛型

2.1 集合使用泛型

2.2 Comparable类和Comparator比较器使用泛型 

1)Comprable类使用泛型

2)Comparator使用泛型

三、自定义泛型(泛型类、泛型接口;泛型方法)

3.1 泛型类/泛型接口

3.2 泛型的继承

 3.3 泛型方法

1)什么是泛型方法

2)泛型方法和如何调用举例

3.4 什么情景下使用泛型类;什么情景下使用泛型方法 

四、泛型在继承上的体现

4.1 <>为父子类关系

4.2 <> 相同,泛型类是父子类关系

五、通配符的使用

5.1 通配符的使用

5.2 使用通配符后对数据读取和写入的要求

5.3 有限制条件的通配符

 5.4 有限制条件的通配符对于读取和写入的要求

六、练习


一、为什么要有泛型

泛型在之前就接触过,例如C++的vector容器

vector<int> v;

 里面的“<int>”就是泛型。

定义:泛型, 就是将数据类型作为参数进行传递。而并非一个类

泛型是JDK 5.0的新特性。主要是为了解决集合中元素类型不确定的问题。在JDK 5.0之前,集合只能设计成Object类,什么元素都能装进来,容易误装不想要的类型。所以,先通过一个例子看一下,如果不使用泛型会发生什么问题:

java表where条件or java select what to run_c++

二、在集合中使用泛型

2.1 集合使用泛型

 那加上泛型以后呢?有两点好处(好处二和好处三其实是一处)

java表where条件or java select what to run_c++_02

 

java表where条件or java select what to run_c++_03

上面以ArrayList为例(单列数据如何使用泛型?),下面再以HashMap为例(双列数据如何使用泛型?)

java表where条件or java select what to run_java表where条件or_04

2.2 Comparable类和Comparator比较器使用泛型 

1)Comprable类使用泛型

java表where条件or java select what to run_泛型_05

2)Comparator使用泛型

java表where条件or java select what to run_java_06

特点01:所有类都可以使用泛型吗?

漏!必须在定义这个类时加上<E>,这个类实例化的时候才能使用泛型。也就是说我们定义一个普普通通的Person类,是无法使用泛型的

java表where条件or java select what to run_泛型_07

 特点02:泛型的类型必须是类,不能是int这样的基本数据类型(but可以使用包装类替换)。

 特点03:没学泛型之前,我们是这样new的:

HashSet set = new HashSet();

 此时,默认添加的元素都是Object类型。 相当于:

HashSet<Object> set = new HashSet<Object>();

特点04:JDK 1.7以后,实例化泛型类的时候,第二个<>可以空着(自动推断)。

HashSet<String> set = new HashSet<空>();

 特点05:泛型不同的引用不能相互赋值(后面继承的时候也会讲到)

java表where条件or java select what to run_html_08

 特点06:static方法中不能用泛型,但【泛型方法】可以声明为static。前者还是那个原因,static方法编译时就被加载进内存,但那个时候泛型的具体类型还没指定。

后者因为:泛型参数是在调用方法时通过参数指定的,并非在实例化类时确定。

特点07:异常类不能是泛型的

java表where条件or java select what to run_java_09

 :在泛型类中如果想要定义【泛型数组】

java表where条件or java select what to run_泛型_10


三、自定义泛型(泛型类、泛型接口;泛型方法)

3.1 泛型类/泛型接口

我们定义一个泛型类Order:

package Generics;

public class Order<T> {
    private String orderName;
    private int orderId;
    private T orderT; //泛型

    public Order(){}

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

    public T getOrderT() {//泛型
        return this.orderT;
    }

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

 泛型类实例化时,将泛型指定为String类型:

Order<String> order = new Order<String>();
        order.setOrderT("现在泛型被指定为String类型");

3.2 泛型的继承

1)继承时指定泛型的类型 

此时子类变为普通类,不是泛型类

public class SubOrder1 extends Order<String>{ //SubOrder1是普通类,不是泛型类了
}

实例化和普通类一样: 

SubOrder1 subOrder1 = new SubOrder1();
        subOrder1.setOrderT("泛型在父类中被指定为String类型");

 2)继承时仍然保留泛型(参照ArrayList<E> extends List<E>)

此时子类是泛型类

public class SubOrder2<T> extends Order<T>{ //SubOrder2<T>是泛型类
}

实例化时需要指定泛型到底是什么类型: 

SubOrder2<String> subOrder2 = new SubOrder2<String>();
        subOrder2.setOrderT("泛型在子类实例化时才被指定为String类型");

还有很多种情况,总结一下:

java表where条件or java select what to run_java表where条件or_11

 

java表where条件or java select what to run_java_12

 3.3 泛型方法

1)什么是泛型方法

首先要明确什么是泛型方法。其实在C++中也接触过,只不过C++中叫模板template

#include <iostream>
using namespace std;
template <typename T>
T add(T a,T b)  //注意形参和返回类型
{   
 return a+b;
} 
void main()
{
    int num1, num2, sum; 
    cin>>num1>>num2;
    sum=add(num1,num2); //用int匹配模版参数T,若sum,num1,num2类型不一致则无法匹配。
    cout<<sum;
}

并不是说泛型类中用到泛型的方法就是泛型方法,拿Order<T>类来说:

java表where条件or java select what to run_java_13

 普通类当中也可以有泛型方法,只要我这个方法的类型不确定,我就可以设置成泛型方法。

所以泛型方法和泛型类没有任何关系,如果在一个泛型类Order<T>中我们想要申明一个泛型方法,为了区分开,就不能用<T>了,可以用<E>

2)泛型方法和如何调用举例

泛型方法举例,作用是将【某类型】的数组放入相应类型的List

public <E> List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> arrayList = new ArrayList<>();
        for(E e:arr){
            arrayList.add(e);
        }
        return arrayList;
    }

注意

java表where条件or java select what to run_java表where条件or_14

 泛型方法的调用:

Order<String> order = new Order<String>();
Integer arr[] = new Integer[]{1,2,3,4};
order.copyFromArrayToList(arr); //调用【泛型方法】的时候,参数arr决定了泛型类型

3.4 什么情景下使用泛型类;什么情景下使用泛型方法 

        我们定义一个对数据库中表进行操作的泛型类,由于数据库中有各种类型的表,所以该类是泛型类。

/**
 * 泛型类:对数据库中【所有类型】表的操作
 */
public class DaoGenerics<T> { //泛型类

    //表中增加一条数据
    public void add(T t){
        //操作省略
    }

    //表中删除一条数据
    public void remove(T t){

    }

    //表中查询一条数据
    public T query(){
        return null;
    }

    //泛型方法
}

 假设数据库中有一张员工表Employee:

/**
 * 一个Empolyee类代表数据库内的一张【员工表】
 */
public class Employee {

}

那么我就要写一个对员工表进行操作的类,继承泛型类,并且指明数据类型为Employee

/**
 * 精确到对数据库中Employee表的操作
 */
public class EmployeeDao extends DaoGenerics<Employee>{  //指定泛型为【Employee】类型

}

 测试:在Employee表中添加一行数据

EmployeeDao employeeDao = new EmployeeDao(); //方式(一)
        employeeDao.add(new Employee());  //直接添加Employee类型的数据

        DaoGenerics<Employee> employeeDao2 = new DaoGenerics<Employee>(); //方式(二)
        employeeDao2.add(new Employee());

四、泛型在继承上的体现

4.1 <>为父子类关系

Object obj = null;
        String str = null;
        obj = str; //√,对象上转型

        ArrayList<Object> list1 = null;
        ArrayList<String> list2 = null;
        list1 = list2; //×,错误

不能互相赋值!!

虽然Object和String是父子类的关系,但list1和list2都是ArrayList类的,list1和list2是并列关系。主要想表达的呢,是不能在实际开发中向下面这样写(一打眼看去,貌似是“多态”):

java表where条件or java select what to run_c++_15

4.2 <> 相同,泛型类是父子类关系

List<String> list3 = null;
        ArrayList<String> list4 = null;
        list3 = list4;

上面这种多态是可以的,因为list3和list4才是“父子接口”的关系 

五、通配符的使用

5.1 通配符的使用

前面(下图)提到的这种“类似多态”是错误的,但是确实我们开发需求的一种。为了解决这种问题,提出了通配符“?”。

java表where条件or java select what to run_c++_15

<?>作为公共父类,可以实现如下的赋值

ArrayList<?> list = null;
        ArrayList<Object> list1 = null;
        ArrayList<String> list2 = null;
        
        list = list1;
        list = list2;

进而,我们就可以这样进行“多态”的方法调用了:

@Test
    public void test07(){
        ArrayList<Object> list1 = null;
        ArrayList<String> list2 = null;

        print(list1);  //多态思想
        print(list2);  //多态思想
    }

    public void print(ArrayList<?> list){
        Iterator<?> iterator = list.iterator();
        while(iterator.hasNext()){
            Object next = iterator.next(); //注意这里的返回类型是Object,而不是通配符“?”
            System.out.println(next);
        }
    }

5.2 使用通配符后对数据读取和写入的要求

读取:允许读取数据。读取的数据类型为Object

写入:不能添加数据。null除外 

理解:为什么使用通配符(注意,这里更严格的说应该是“无限制”的通配符)后不能添加数据呢?

首先要明确,list和list3是同一个类的对象,并不是父子类关系。

因为<?>可以匹配所有类型,话句话说,?可以add所有类型的数据,以下面的例子为例,list3本来规定了只能添加String类型的数据,list=list3以后,list再去添加一个Integer类型的数据,这样list3不就白限制类型了吗,不就违背了使用泛型的意义了吗,所以直接不让?添加数据。

java表where条件or java select what to run_泛型_17

 了解了使用通配符后的读取和写入要求有什么用呢?

在我们上面写的print个方法中,只能用list读取数据,不能去改动或者添加数据。

5.3 有限制条件的通配符

其实就是限制通配符的“通配”范围。

<? extends A>,可以“通配”A类,或者A类的子类

<? superA>,可以“通配”A类,或者A类的父类

java表where条件or java select what to run_html_18

 5.4 有限制条件的通配符对于读取和写入的要求

理解记忆!

有一点需要注意的是,有限制条件的通配符可以添加数据了!!原因就在于,有了限制条件(自己看下面的例子去悟吧)

ArrayList<? extends Person> list1 = null;
        ArrayList<? super Person> list2 = null;

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

        list1 = list3;  //√
        list1 = list4;  //√
        //list1 = list5;  //×

        //list2 = list3;  //×
        list2 = list4;  //√
        list2 = list5;  //√

        //1) 读取,extends -------> Person和Person的子类,可以用Person或者Object去接收
        Object obj = list1.get(0);
        Person person = list1.get(0);
        //Student stu = list1.get(0);  //×,和对象不能【下转】是一个道理

        //2) 读取,super -------> Person和Person的父类,只能用Object接收
        Object obj1 = list2.get(0);
        //Person per = list2.get(0); //×,和对象不能【下转】是一个道理

        //3)写入,extends ------->
        //list1.add(new Person());  //×,Person不能赋给Student,和对象不能【下转】是一个道理
        //list1.add(new Student()); //×,Student不能赋给Person的其他子类,和对象不能【下转】是一个道理
        
        //4) 写入,super
        list2.add(new Person());
        list2.add(new Student());

六、练习

1. 体会泛型的嵌套

三层嵌套。

注意ArrayList<Citizen>一旦带上<Citizen>,就要从头到尾带着<Citizen>。

ArrayList<Citizen> familyList = new ArrayList<Citizen>();
        familyList.add(new Citizen("父亲"));
        familyList.add(new Citizen("母亲"));
        familyList.add(new Citizen("女儿"));
        familyList.add(new Citizen("儿子"));

        HashMap<String, ArrayList<Citizen>> houseHoldRegister = new HashMap<>(); //map<户主,家庭成员>
        houseHoldRegister.put("父亲",familyList);

        /**
         * 遍历
         */
        Set<Map.Entry<String, ArrayList<Citizen>>> set = houseHoldRegister.entrySet();
        Iterator<Map.Entry<String, ArrayList<Citizen>>> it = set.iterator();
        while(it.hasNext()){
            Map.Entry<String,ArrayList<Citizen>> entry= it.next();
            System.out.println("1)户主:"+entry.getKey());
            System.out.println("2)家庭成员:");
            ArrayList<Citizen> value = entry.getValue();
            for(Citizen c:value){
                System.out.println(c.getName());
            }
        }
public class Citizen {
    String name;

    public Citizen(){

    }

    public Citizen(String name){
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

2. 有限制的泛型类Person<T extends Info>

java表where条件or java select what to run_java_19

 一个【人的信息】接口

/**
 * 只有实现此接口的子类才表示【人的信息
 */
public interface Info {
}

一个ContactInfo类实现Info接口,存储联系方式

public class ContactInfo implements Info{
    private String address; //联系地址
    private String telephone; //联系电话

    public ContactInfo(){

    }
    public ContactInfo(String address,String telephone){
        this.address = address;
        this.telephone = telephone;
    }
    public void setAddress(String address){
        this.address = address;
    }
    public void setTelephone(String telephone){
        this.telephone = telephone;
    }
    public String getAddress(){
        return this.address;
    }
    public String getTelephone(){
        return this.telephone;
    }
    @Override
    public String toString(){
        return "ContactInfo [address="+this.address+",telephone"+this.telephone+"]";
    }
}

一个PersonalInfo类实现Info接口,存储基本信息

public class PersonalInfo implements Info{
    private String name; //姓名
    private int age; //年龄

    public PersonalInfo(){}
    public PersonalInfo(String name, int age){
        this.name = name;
        this.age = age;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
    @Override
    public String toString(){
        return "PersonalInfo [name="+this.name+",age="+this.age+"]";
    }

}

Person类作为一个泛型类,其泛型还是有限制的泛型,只能是实现Info接口的子类(注意,这里Info虽然是个接口不是个类,依然可以使用extends关键字)。

public class Person <T extends Info>{
    private T info;
    public Person(){}
    public Person(T info){
        this.info = info;
    }
    public void setInfo(T info){
        this.info = info;
    }
    public T getInfo(){
        return this.info;
    }
    @Override
    public String toString(){
        return "Person [info=" + info +"]";
    }
}

测试类:

public class GeneticsTest {
    public static void main(String[] args) {
        Person<ContactInfo> per = new Person<ContactInfo>(new ContactInfo("北京市","22225555"));
        System.out.println(per); //使用我们自己重写的toString方法
        //输出:
        //Person [info=ContactInfo [address=北京市,telephone22225555]]

        Person<PersonalInfo> per2 = new Person<PersonalInfo>(new PersonalInfo("LN",18));
        System.out.println(per2);
        //输出:
        //Person [info=PersonalInfo [name=LN,age=18]]
    }
}

3. 自定义泛型类例题

java表where条件or java select what to run_java表where条件or_20

 但是这里的User类并不能说明这个题的用意(用意就是泛型类DAO可以操作所有类型的用户),所以我给他改成一个歌手类Singer和一个演员类Actor。

DAO.java

public class DAO<T>{
    private Map<String, T> map = new HashMap<>();

    //保存 T 类型的对象到map中
    public void save(String id, T entity){
        map.put(id,entity);
    }
    //从map中获取id对应的对象
    public T get(String id){
        return map.get(id);
    }
    //替换 map中key为id的内容,改为entity对象
    public void update(String id, T entity){
        if(map.containsKey(id)){
            map.put(id,entity);
        }
    }
    //返回 map中存放的所有T对象
    public List<T> list(){
        ArrayList<T> arrayList = new ArrayList<>();
        Collection<T> values = map.values();
        for(T t: values){
            arrayList.add(t);
        }
        return arrayList;
    }
    //删除指定id对象
    public void delete(String id){
        map.remove(id);
    }
}

Singer.java

public class Singer {
    private String name;

    public Singer() {
    }

    public Singer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Singer{" +
                "name='" + name + '\'' +
                '}';
    }
    //因为Singer类要作为map的value,所以Singer类要重写equals方法

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Singer singer = (Singer) o;

        return name != null ? name.equals(singer.name) : singer.name == null;
    }

//    @Override
//    public int hashCode() {
//        return name != null ? name.hashCode() : 0;
//    }
}

演员类和歌手类一样,省略。

DAOTest.java

public void test01(){
        /**
         * 对歌手的操作:new DAO<Singer>()
         */
        DAO<Singer> singerDAO = new DAO<Singer>();
        singerDAO.save("001", new Singer("刘德华"));
        singerDAO.save("002", new Singer("林俊杰"));
        singerDAO.save("003", new Singer("汪苏泷"));
        singerDAO.save("004", new Singer("蔡健雅"));
        List<Singer> singerList = singerDAO.list();
        System.out.println(singerList);
        //输出:[Singer{name='刘德华'}, Singer{name='林俊杰'}, Singer{name='汪苏泷'}, Singer{name='蔡健雅'}]
        /**
         * 对演员的操作
         */
        DAO<Actor> actorDAO = new DAO<Actor>();
        actorDAO.save("001",new Actor("沈腾"));
        actorDAO.save("002",new Actor("吴京"));
        actorDAO.save("003",new Actor("黄渤"));
        actorDAO.delete("002");
        List<Actor> actorList = actorDAO.list();
        System.out.println(actorList);
        //输出:[Actor{name='沈腾'}, Actor{name='黄渤'}]
    }