我学习 B站 韩顺平 老师课程的学习笔记

泛型

泛型引入

需要泛型的体现

使用代码

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
import java.util.ArrayList;

/**
* @author 张永锐
*/
public class Needgenericity {
public static void main(String[] args) {
// //传统
// ArrayList arrayList = new ArrayList();
// arrayList.add(new Dog("小黄",10));
// arrayList.add(new Dog("旺财",5));
// arrayList.add(new Dog("牛狗",6));
// //加入不小心添加了一只猫
// arrayList.add(new Cat("招财猫",8));//因为ArrayList实现了泛型所以没有错误
//
// //遍历,当添加了猫进去后就会出问题,不能将Cat转型为Dog(只能运行到Cat这里才会抛出异常,编译器开始不报错),所以前面需要泛型,限制添加类型
// for(Object o : arrayList){
// Dog dog = (Dog) o;
// System.out.println("名字:" + dog.getName() +" 年龄"+ dog.getAge());
// }

//使用泛型
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("小黄",10));
arrayList.add(new Dog("旺财",5));
arrayList.add(new Dog("牛狗",6));
//arrayList.add(new Cat("招财猫",8));//报错,编译时就会报错,表示不能添加
//使用泛型后遍历
for(Dog dog : arrayList){
System.out.println("名字:" + dog.getName() + " 年龄:" + dog.getAge());
}
}
}
class Dog {
private String name;
private int age;

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

public String getName() {return name;}

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

public int getAge() {return age;}

public void setAge(int age) {this.age = age;}
}
class Cat{
private String name;
private int age;

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

public String getName() {return name;}

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

public int getAge() {return age;}

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

代码说明

  • 使用ArrayList<Dog>()替换原本的ArrayList()表示存放到ArrayList集合中的元素只能是Dog的对象实例

  • 当编译器发现添加的类型不满足要求就会提前报错(不会在等到运行时再报错)

  • 在遍历时可以直接取出Dog类型,而不是Object
    原本:

    1
    for(Object obj : arrayList) {...}

    使用泛型后:

    1
    for(Dog dog : arrayList) {...}
  • ArrayList的原型为:

    1
    public class ArrayList<E> {...}

    这个E称为泛型,当上面使用泛型后,那么E就全用Dog来表示了
    E相当于C++的模板中的Type,声明是写就好了,可以不仅仅是E也可以是Q,A,R等任意名字,用来代表传入的类型

泛型基本介绍

  1. 泛型有称为参数化类型,时JDK5.0出现的新特性,解决数据类型的安全性问题
  2. 在类声明或实例化时只要指定好需要的具体类型即可
  3. java泛型可以保证如果程序在编译时没有发出警告,那么在运行时也不会产生ClassCastException异常
  4. java泛型也会放代码更简洁,健壮
  5. 泛型的作用是:可以在类声明时同时通过一个标识符表示类中的某个属性的类型,或是某个方法的返回值的类型,或者是参数模型

代码演示

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
public class Generic03 {
public static void main(String[] args) {

//注意:E 具体的数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
Person<String> Person = new Person<String>("牛人");//构造器传参类型不同也会报错
/**
* 上面的初始化相当于调用:
* class Person {
* private String s;
*
* public Person(String s) {
* this.s = s;
* }
* public String getS() {
* return s;
* }
* }
*/
Person<Integer> integerPerson = new Person<Integer>(100);
/**
* 同样相当于:
* class Person {
* private Integer s;
*
* public Person(Integer s) {
* this.s = s;
* }
* public Integer getS() {
* return s;
* }
* }
*/
}
}
class Person<E> {//E是什么类型都可以(包括之定义类型)
private E s;//E类型的s对象,可以直接把E就看作是一个类型

public Person(E s) {//可以是参数类型
this.s = s;
}
public E getS() {//可以是返回类型
return s;
}
}

当然也可以有多个类型:

1
2
3
class Person<E,T,K> {
...;
}

泛型注意细节

  1. 假设有interface List {},public class HashSet{}…..</br>说明:T,E只能为引用类型,不能是基本类型,所以:

    1
    2
    List<Integer> list = new ArrayList<Integer>();//正确
    List<int> list = new ArrayList<int>();//错误
  2. 在指定泛型具体类型后,可以传入该类型或该类型的子类型

  3. 泛型使用形式:

    1
    2
    List<Integer> list1 = new ArrayList<Integer>();
    List<Integer> list2 = new ArrayList<>();

    如果像后者一样写,看等号右边默认给它的泛型是Object,但是等号左边给定了是Integer,所以实际上泛型类型是Integer,若等号左边也没规定,就会像之前没有引入泛型一样,为默认的Object

练习(重要)

注意多看,认真看sort()写得很好,尤其是MyDate的compareTo()的封装

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
* @author 张永锐
*/
@SuppressWarnings({"all"})
public class GenericExercise {
public static void main(String[] args) {
List<Emp> list = new ArrayList<>();
list.add(new Emp("Smith",2500.1,new MyDate(2003,12,5)));
list.add(new Emp("Smith",2500.1,new MyDate(2002,12,5)));
list.add(new Emp("Rose",25000.4,new MyDate(2004,2,15)));
list.add(new Emp("Jack",2503.13,new MyDate(2003,1,6)));

list.sort(new Comparator<Emp>() {
@Override
public int compare(Emp emp, Emp t1) {
if(!((emp instanceof Emp) && (t1 instanceof Emp))){
System.out.println("类型不正确");
return 0;
}

//比较姓名
int i = emp.getName().compareTo(t1.getName());
if(i != 0){
return i;
}

//将生日的比较封装到MyDate的类定义里去
return emp.getBirthday().compareTo(t1.getBirthday());
}
});
System.out.println("list = " + list);
}
}

class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;

public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}

public int getYear() {
return year;
}

public void setYear(int year) {
this.year = year;
}

public int getMonth() {
return month;
}

public void setMonth(int month) {
this.month = month;
}

public int getDay() {
return day;
}

public void setDay(int day) {
this.day = day;
}

@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
"}";
}

@Override
public int compareTo(MyDate o) {
int yd =year - o.getYear();
if(yd != 0){
return yd;
}
int md = month - o.getMonth();
if(md != 0){
return md;
}
int dd;
return dd = day - o.getDay();
}
}
class Emp {
private String name;
private double sal;
private MyDate birthday;

public Emp(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}

@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
"}\n";
}

public String getName() {
return name;
}

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

public double getSal() {
return sal;
}

public void setSal(double sal) {
this.sal = sal;
}

public MyDate getBirthday() {
return birthday;
}

public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
}

自定义泛型类

基本语法

1
2
3
class 类名 <T,R> {//可以是多个
成员;
}

注意细节

  1. 普通成员可以使用泛型
  2. 使用泛型的数组,不能初始化(不能new,意思是不能直接实例化,因为不知道类型,不知道开辟多大空间,避免之后空间冲突)
  3. 静态方法中不能使用泛型(因为静态是和类相关的,在类被加载的时候,对象都还没创建,JVM还不知道什么类型,就无法在静态方法中将类型改变为指定类型,所以JVM不能完成这样的初始化,就会报错)
  4. 泛型的类型,是在创建对象时就确定的(因为创建对象需要确定类型)
  5. 如果在创建对象时,没有指定类型,默认为Object

代码演示

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
//Tiger 类后面有<>所以Tiger是一个泛型类
//T,R,M时泛型常用的标识符,一般标识符都是大写
//泛型标识符可以有多个
class Tiger <T,R,M> {
String name;
//属性使用泛型
T t;
R r;
M m;

//普通方法可以使用泛型
public void f1 (M m1) {}
//public static void f2 (M m2){}

//方法使用泛型(使用泛型作为参数)
public Tiger(String name, T t, R r, M m) {
this.name = name;
this.t = t;
this.r = r;
this.m = m;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//方法使用泛型(使用泛型作为返回值)
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
}

自定义泛型接口

基本语法

interface 接口名 {}

注意细节

  1. 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
  2. 泛型接口的类型,在继承接口或者实现接口时确定
  3. 没有指定类型时,默认为Object

代码演示

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
interface IUsb<U,R> {
//普通方法可以使用接口泛型
R get(U u);

void hi(R r);

void run (R r1,R r2,U u1 ,U u2);

default R method(U u){
return null;
}
}
//下面这样没有直接说明类型就会报错,一定要先将类型说明清楚
//class AA implements IUsb<>{
//
//}
class AB implements IUsb{//但是什么都不写也可以,但是是默认的Object,不好(最好直接指明)
@Override
public Object get(Object o) {
return null;
}
@Override
public void hi(Object o) {}
@Override
public void run(Object r1, Object r2, Object u1, Object u2) {}
}

//实现IUsb的时候自动填入对应类型
//U指定为String,R指定为Double
class AA implements IUsb<String, Double>{
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {}
@Override
public void run(Double r1, Double r2, String u1, String u2) {}
}
//接口只能继承接口不能实现接口,和类实现接口差不多,但可以不用实现接口方法(可以等着实现此接口的类来实现)
interface IA extends IUsb<String, Double> {}

自定义泛型方法

基本语法

修饰符 返回类型 方法名(参数列表){…}

注意细节

  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat(E e){}这样,修饰符后面没有,所以eat()就不是泛型方法,而是使用泛型的方法
  4. 一般时参数类型是什么标识符,泛型的修饰符后面就写什么标识符

代码演示

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
public class SelfGeneric03 {
public static void main(String[] args) {
Car car = new Car();
car.fly("宝马", 100);//当调用方法时,传入参数,编译器,就会确定类型
System.out.println("=======");
car.fly(300, 100.1);//当调用方法时,传入参数,编译器,就会确定类型

//测试
//T->String, R-> ArrayList
Fish<String, ArrayList> fish = new Fish<>();
fish.hello(new ArrayList(), 11.3f);
}
}

//泛型方法,可以定义在普通类中, 也可以定义在泛型类中
class Car {//普通类

public void run() {//普通方法
}
//说明 泛型方法
//1. <T,R> 就是泛型
//2. 是提供给 fly使用的
public <T, R> void fly(T t, R r) {//泛型方法
System.out.println(t.getClass());//String
System.out.println(r.getClass());//Integer
}
}

class Fish<T, R> {//泛型类
public void run() {//普通方法
}
public<U,M> void eat(U u, M m) {//泛型方法
}
//说明
//1. 下面hi方法不是泛型方法
//2. 是hi方法使用了类声明的 泛型
public void hi(T t) {
}
//泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
public<K> void hello(R r, K k) {
System.out.println(r.getClass());//ArrayList
System.out.println(k.getClass());//Float
}
}

泛型的继承和通配符说明

  1. 泛型不具备继承性
    List<Object> list = new ArrayList<String>();//报错
    意思是尖括号里的类型要前后一致,继承也不行
  2. <?>:支持任意泛型类型
  3. <? extends A>:支持A类以及A类的子类,规定了泛型的上限(为A类,比A高就不能用了)
  4. <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限(为A类,比A低就不能用了)

演示代码

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
import java.util.ArrayList;
import java.util.List;

/**
* @author 张永锐
*/
@SuppressWarnings({"all"})
public class GenericExtends {
public static void main(String[] args) {
Object o = new String("xx");
//List<Object> list = new ArrayList<String>();//报错,原因是泛型不具备继承性

//举例使用下面的三个方法
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<Integer> list3 = new ArrayList<>();
List<A> list4 = new ArrayList<>();
List<B> list5 = new ArrayList<>();
List<C> list6 = new ArrayList<>();

//如果是 List<?> c 类型的参数就可都可以
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
printCollection1(list6);

//如果是 List<? extends A> c 类型的参数,则只能A以及A的子类能够传入,所以前三个均报错
// printCollection2(list1);
// printCollection2(list2);
// printCollection2(list3);
printCollection2(list4);
printCollection2(list5);
printCollection2(list6);

//如果是 List<? super A> c 类型的参数,则只能A以及A的父类才能够参入参数,所以list2\list3\list5\list6均报错
//list1为Object类型是A的父类,list4是A类,所以吧报错
printCollection3(list1);
// printCollection3(list2);
// printCollection3(list3);
printCollection3(list4);
// printCollection3(list5);
// printCollection3(list6);

}
//写几个测试方法
//List<?>说明:标识任意的泛型类型都能接收
public static void printCollection1(List<?> c){
for(Object object : c){ //使用通配符,取出是就是Object
System.out.println(object);
}
}

//支持A类以及A类的子类,规定了泛型的上限
public static void printCollection2(List<? extends A> c){
for(Object object : c){
System.out.println(object);
}
}

//支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
public static void printCollection3(List<? super A> c){
for(Object object : c){
System.out.println(object);
}
}
}
class A{}
class B extends A{}
@SuppressWarnings({"unused"})
@Deprecated
class C extends B{}

泛型进阶尝试

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
107
108
109
110
111
112
113
114
115
116
117
import java.util.*;

/**
* @author 张永锐
*/
public class GenericExercise01 {
public static void main(String[] args) {
DAO<User> userDAO = new DAO<User>();
//使用save
userDAO.save("牛人", new User(123,25,"牛人"));
userDAO.save("Jack", new User(125,24,"Jack"));
userDAO.save("Smith", new User(133,19,"Smith"));
userDAO.save("Tom", new User(159,20,"Tom"));
userDAO.save("Mary", new User(115,49,"Mary"));

//使用get
System.out.println("----使用get----");
System.out.println(userDAO.get("Jack"));
User user = userDAO.get("Tom");
System.out.println(user);

//使用update
System.out.println("----使用update----");
userDAO.update("牛人",new User(110,48,"牛人他爹"));
System.out.println(userDAO.get("牛人"));

//使用list
System.out.println("----使用list----");
List<User> ts = userDAO.list();
for(Object obj : ts){
System.out.println(obj);
}

//使用delete
System.out.println("----使用delete----");
userDAO.delete("牛人");
ts = userDAO.list();
for(Object obj : ts){
System.out.println(obj);
}
}
}
class DAO<T> {
private Map<String, T> map = new HashMap<>();//注意要让map指向对象实例(之前没有等号后面一段,程序就报错了)

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);
}
//要遍历map[k-v],将map中的所以value(T entity),封装到ArrayList中
public List<T> list(){
//创建ArrayList
ArrayList<T> ts = new ArrayList<>();

//遍历map
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
String s = (String) o;
ts.add(get(s));
}
return ts;
}
public void delete(String id){
map.remove(id);
}
}
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 + '\'' +
'}';
}
}