泛型(Generic)

作用

  • 解决元素存储的安全性问题
  • 解决获取数据元素时,需要类型强转的问题
1
2
3
4
5
6
7
8
9
10
11
12
List list= new ArrayList<>();
list.add(12);
list.add(24);
list.add(48);
// 1、没有使用泛型,任何Object及其子类的对象都可以添加进来
list.add(new String("AA"));

for (int i = 0; i < list.size(); i++){
// 2、强转为int型时,可能报ClassCastException异常
int score = (Integer)list.get(i);
System.out.println(score);
}
1
2
3
4
5
6
7
8
9
List<Integer> list= new ArrayList<>();
list.add(12);
list.add(24);
list.add(48);

for (int i = 0; i < list.size(); i++){
int score = list.get(i);
System.out.println(score);
}

使用

泛型的声明

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

泛型的实例化

​ 一定要在类名后面指定类型参数的值(类型)。如:

1
2
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();

​ T只能是类,不能用基本数据类型填充。

几个重要的使用

  • 在集合中使用泛型
  • 自定义泛型类
  • 泛型方法
  • 泛型接口

总结

​ 泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

泛型类(含集合类)

  • 对象实例化时不指定泛型,默认为:Object
  • 泛型不同的引用不能相互赋值
  • 加入集合中的对象类型必须与指定的泛型类型一致
  • 静态方法中不能使用类的泛型
  • 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象
  • 不能在catch中使用泛型
  • 从泛型类派生子类,泛型类型需具体化

把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想

自定义泛型类

1
2
3
4
5
6
7
8
9
10
11
class Person<T>{
//使用T类型定义变量
private T info;
//使用T类型定义一般方法
public T getInfo(){
return info;
}
public void setInfo(T info){
this.info = info;
}
}

泛型方法

​ 方法,也可以被泛型化,不管此时定义在其中的类是不是泛型化的。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

  • 泛型方法的格式:

[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常

1
2
3
4
5
6
public class DAO {
public <E> E get(int id, E e){
E result = null;
return result;
}
}

泛型和继承的关系

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!比如:String是Object的子类,但是List<String>并不是List<Object>的子类。

通配符

  • 使用类型通配符:?

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

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

    • 唯一的例外是null,它是所有类型的成员。

有限制的通配符

  • <?>:允许所有泛型的引用调用
  • <? extends Number> : **(无穷小, Number]**,只允许泛型为Number及Number子类的引用调用
  • <? super Number> : **[Number , 无穷大)**,只允许泛型为Number及Number父类的引用调用
  • <? extends Comparable>:只允许泛型为实现Comparable接口的实现类的引用调用

应用

练习1

​ 用户在设计类的时候往往会使用类的关联关系,例如,一个人中可以定义一个信息的属性,但是一个人可能有各种各样的信息(如联系方式、基本信息等),所以此信息属性的类型就可以通过泛型进行声明,然后只要设计相应的信息类即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class GenericPerson {
public static void main(String[] args) {
Person<Contact> person = null; // 声明person对象
person = new Person<>(new Contact("南京市","18866668888","10088"));
System.out.println(person);

Person<Introduction> person2 = null; // 声明person对象
person2 = new Person<>(new Introduction("小新","男",22));
System.out.println(person2);

}
}

interface Info{ // 只有此接口的子类才是表示人的信息
}

// 表示联系方式
class Contact implements Info{
private String address ; // 联系地址
private String telephone ; // 联系方式
private String zipcode ; // 邮政编码
public Contact(String address,String telephone,String zipcode){
this.address = address;
this.telephone = telephone;
this.zipcode = zipcode;
}
public void setAddress(String address){
this.address = address ;
}
public void setTelephone(String telephone){
this.telephone = telephone ;
}
public void setZipcode(String zipcode){
this.zipcode = zipcode;
}
public String getAddress(){
return this.address ;
}
public String getTelephone(){
return this.telephone ;
}
public String getZipcode(){
return this.zipcode;
}
@Override
public String toString() {
return "Contact [address=" + address + ", telephone=" + telephone
+ ", zipcode=" + zipcode + "]";
}
}

// 个人信息
class Introduction implements Info{
private String name ; // 姓名
private String sex ; // 性别
private int age ; // 年龄
public Introduction(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}
public void setName(String name){
this.name = name ;
}
public void setSex(String sex){
this.sex = sex ;
}
public void setAge(int age){
this.age = age ;
}
public String getName(){
return this.name ;
}
public String getSex(){
return this.sex ;
}
public int getAge(){
return this.age ;
}
@Override
public String toString() {
return "Introduction [name=" + name + ", sex=" + sex + ", age=" + age
+ "]";
}
}

class Person<T extends Info>{
private T info ;
public Person(T info){ //通过构造方法设置信息属性内容
this.info = info;
}
public void setInfo(T info){
this.info = info ;
}
public T getInfo(){
return info ;
}
@Override
public String toString() {
return "Person [info=" + info + "]";
}

// public String toString(){ // 覆盖Object类中的toString()方法
// return this.info.toString() ;
// }
}

练习2

  • 定义一个Employee类,该类包含:private成员变量name、age、birthday,其中 birthday 为 MyDate 类的对象;并为每一个属性定义 getter、setter 方法;并重写 toString 方法输出 name、age、birthday
  • MyDate类包含:private成员变量month、day、year;并为每一个属性定义 getter、setter 方法;
  • 创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(TreeSet 需使用泛型来定义),分别按以下两种方式对集合中的元素进行排序,并遍历输出:
    • 使Employee 继承 Comparable 接口,并按 name 排序
    • 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TestTreeSet {
public static void main(String[] args) {

// 1、自然排序
// Employee employee = new Employee("小新",22,new MyDate(1999,10,29));
// Employee employee2 = new Employee("小鑫",24,new MyDate(1997,6,13));
// Employee employee3 = new Employee("小伟",21,new MyDate(2000,10,28));
// Employee employee4 = new Employee("小宇",23,new MyDate(1998,7,12));
// Employee employee5 = new Employee("小创",22,new MyDate(1999,3,18));
//
// TreeSet<Employee> treeSet = new TreeSet<>();
// treeSet.add(employee);
// treeSet.add(employee2);
// treeSet.add(employee3);
// treeSet.add(employee4);
// treeSet.add(employee5);
//
// Iterator<Employee> iterator = treeSet.iterator();
// while (iterator.hasNext()){
// System.out.println(iterator.next());
// }


// 2、定制排序
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof L07.exer2.Employee2 && o2 instanceof L07.exer2.Employee2){
L07.exer2.Employee2 e1 = (L07.exer2.Employee2) o1;
L07.exer2.Employee2 e2 = (L07.exer2.Employee2) o2;
L07.exer2.MyDate birthday1 = e1.getBirthday();
L07.exer2.MyDate birthday2 = e2.getBirthday();
if (birthday1.getYear() != birthday2.getYear()){
return birthday1.getYear() - birthday2.getYear();
}
else {
if (birthday1.getMonth() != birthday2.getMonth()){
return birthday1.getMonth() - birthday2.getMonth();
}
else {
return birthday1.getDay() - birthday2.getDay();
}
}
}
return 0;
}
};
Employee2 employee = new Employee2("小新",22,new MyDate(1999,10,29));
Employee2 employee2 = new Employee2("小鑫",24,new MyDate(1997,6,13));
Employee2 employee3 = new Employee2("小伟",21,new MyDate(2000,10,28));
Employee2 employee4 = new Employee2("小宇",23,new MyDate(1998,7,12));
Employee2 employee5 = new Employee2("小创",22,new MyDate(1999,3,18));

TreeSet<Employee2> treeSet = new TreeSet<Employee2>(comparator);
treeSet.add(employee);
treeSet.add(employee2);
treeSet.add(employee3);
treeSet.add(employee4);
treeSet.add(employee5);

Iterator<Employee2> iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}

练习3

  • 定义个泛型类 DAO<T>,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。分别创建以下方法:

    • public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
    • T get(String id):从 map 中获取 id 对应的对象
    • void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象
    • List<T> list():返回 map 中存放的所有 T 对象
    • void delete(String id):删除指定 id 对象
  • 定义一个 User 类:该类包含:private成员变量(int类型)id、age;(String 类型)name

  • 创建 DAO 类的对象, 分别调用其 save、get、update、list、delete 方法来操作 User 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DAO<T> {
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){
map.put(id, entity);
}

public List<T> list(){
List<T> list = new ArrayList<>();
for (String s : map.keySet()){
list.add(map.get(s));
}
return list;
}

public void delete(String id){
map.remove(id);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.util.Objects;

public class User {
private int id;
private int age;
private String name;

public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

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

@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return getId() == user.getId() && getAge() == user.getAge() && Objects.equals(getName(), user.getName());
}

@Override
public int hashCode() {
return Objects.hash(getId(), getAge(), getName());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.HashMap;
import java.util.List;

public class TestDAO {
public static void main(String[] args) {
DAO<User> dao = new DAO<>();
dao.map = new HashMap<>();
dao.save("1001",new User(1,22,"小新"));
dao.save("1002",new User(2,23,"小鑫"));
dao.save("1003",new User(3,21,"小伟"));
User user = dao.get("1002");
System.out.println(user);
dao.update("1002",new User(2,26,"黄景瑜"));
dao.delete("1003");
List<User> list = dao.list();
System.out.println(list);
}
}