javaSE 集合框架—— 泛型
Posted 玛丽莲茼蒿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javaSE 集合框架—— 泛型相关的知识,希望对你有一定的参考价值。
目录
2.2 Comparable类和Comparator比较器使用泛型
一、为什么要有泛型
泛型在之前就接触过,例如C++的vector容器
vector<int> v;
里面的“<int>”就是泛型。
定义:泛型, 就是将数据类型作为参数进行传递。而并非一个类
泛型是JDK 5.0的新特性。主要是为了解决集合中元素类型不确定的问题。在JDK 5.0之前,集合只能设计成Object类,什么元素都能装进来,容易误装不想要的类型。所以,先通过一个例子看一下,如果不使用泛型会发生什么问题:
二、在集合中使用泛型
2.1 集合使用泛型
那加上泛型以后呢?有两点好处(好处二和好处三其实是一处)
上面以ArrayList为例(单列数据如何使用泛型?),下面再以HashMap为例(双列数据如何使用泛型?)
2.2 Comparable类和Comparator比较器使用泛型
1)Comprable类使用泛型
2)Comparator使用泛型
特点01:所有类都可以使用泛型吗?
漏!必须在定义这个类时加上<E>,这个类实例化的时候才能使用泛型。也就是说我们定义一个普普通通的Person类,是无法使用泛型的
特点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:泛型不同的引用不能相互赋值(后面继承的时候也会讲到)
特点06:static方法中不能用泛型,但【泛型方法】可以声明为static。前者还是那个原因,static方法编译时就被加载进内存,但那个时候泛型的具体类型还没指定。
后者因为:泛型参数是在调用方法时通过参数指定的,并非在实例化类时确定。
特点07:异常类不能是泛型的
:在泛型类中如果想要定义【泛型数组】
三、自定义泛型(泛型类、泛型接口;泛型方法)
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类型");
还有很多种情况,总结一下:
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>类来说:
普通类当中也可以有泛型方法,只要我这个方法的类型不确定,我就可以设置成泛型方法。
所以泛型方法和泛型类没有任何关系,如果在一个泛型类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;
注意:
泛型方法的调用:
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是并列关系。主要想表达的呢,是不能在实际开发中向下面这样写(一打眼看去,貌似是“多态”):
4.2 <> 相同,泛型类是父子类关系
List<String> list3 = null;
ArrayList<String> list4 = null;
list3 = list4;
上面这种多态是可以的,因为list3和list4才是“父子接口”的关系
五、通配符的使用
5.1 通配符的使用
前面(下图)提到的这种“类似多态”是错误的,但是确实我们开发需求的一种。为了解决这种问题,提出了通配符“?”。
<?>作为公共父类,可以实现如下的赋值
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不就白限制类型了吗,不就违背了使用泛型的意义了吗,所以直接不让?添加数据。
了解了使用通配符后的读取和写入要求有什么用呢?
在我们上面写的print个方法中,只能用list读取数据,不能去改动或者添加数据。
5.3 有限制条件的通配符
其实就是限制通配符的“通配”范围。
<? extends A>:≤,可以“通配”A类,或者A类的子类
<? superA>:≥,可以“通配”A类,或者A类的父类
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>
一个【人的信息】接口
/**
* 只有实现此接口的子类才表示【人的信息
*/
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. 自定义泛型类例题
但是这里的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);
//输出:[Singername='刘德华', Singername='林俊杰', Singername='汪苏泷', Singername='蔡健雅']
/**
* 对演员的操作
*/
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);
//输出:[Actorname='沈腾', Actorname='黄渤']
以上是关于javaSE 集合框架—— 泛型的主要内容,如果未能解决你的问题,请参考以下文章