Java笔记——泛型

泛型 Generic

泛型的理解和好处

  • 使用传统方法的问题

    1. 不能对加入到集合 ArrayList 中的数据类型进行约束(不安全)
    2. 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
  • 用泛型来解决

1
ArrayList<Dog> arrayList = new ArrayList<Dog>(); // 在数据类型后边加上泛型的标识
  1. 如果编译器发现添加的类型不满足要求,就会报错
  2. 在遍历的时候,可以直接取出Dog类型而不是Object, 不用进行向下转型
  3. public class ArrayList<E> {} E称为泛型, 那么 Dog \(\rightarrow\) E

泛型说明

int a = 10; 泛(广泛)型(类型) E => Integer, String, Dog

  1. 泛型又称参数化类型,是 Jdk5.0 出现的新特性, 解决数据类型的安全性问题 【泛型可以理解为接收数据类型的数据类型】
  2. 在类声明或实例化时只要指定好需要的具体的类型即可。
  3. Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁、健壮
  4. 泛型的作用是: 可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
Person<String> p = new Person<String>("hello");

class Person<E> {
E s; // E表示s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间就确定E是什么类型 getClass

public Person(E s) { // 参数类型
this.s = s;
}

public E f() { // 返回类型
return s;
}
}

泛型语法

  • 泛型的声明 interface接口<T>和class类<K,V>{} //比如: List, ArrayList 说明:

    1. 其中,T,K,V不代表值,而是表示类型。
    2. 任意字母都可以。常用T表示,是Type的缩写
  • 泛型的实例化 要在类名后面指定类型参数的值(类型)。如:

    1. List<String> strList = new ArrayList<String>();
    2. Iterator<Customer> iterator = customers.iterator();
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
public class Generic01 {
public static void main(String[] args) {
// ==============HashSet================
HashSet<Stu> stus = new HashSet<>();
Stu s1 = new Stu("jojo", 18);
Stu s2 = new Stu("kiki", 5);
Stu s3 = new Stu("lala", 8);
stus.add(s1);
stus.add(s2);
stus.add(s3);

// 1.1
for (Stu stu : stus) {
System.out.println(stu);
}
System.out.println("+++++++++++++++++++");
// 1.2
Iterator<Stu> iterator = stus.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

//==============HashMap==================
HashMap<String, Stu> map = new HashMap<>();
map.put("jojo", s1);
map.put("kiki", s2);
map.put("lala", s3);
System.out.println("+++++++++++++++++++");
// 2.1
for (String s : map.keySet()) {
System.out.println(s + " - " + map.get(s));
}
System.out.println("+++++++++++++++++++");
// 2.2
Iterator<Map.Entry<String, Stu>> entryIterator = map.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Stu> next = entryIterator.next();
System.out.println(next.getKey() + " - " + next.getValue());
// System.out.println(entryIterator.next());
}
}
}

class Stu {
String name;
int age;

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

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

泛型使用细节

  1. interface List<T>{}, public class HashSet<E>{} ... 等等 说明: T, E 只能是引用类型 看看下面语句是否正确?
1
2
List<Integer> list = new ArrayList<Integer>(); // OK
List<int> list2 = new ArrayList<int>(); // 错误
  1. 在指定泛型具体类型后, 可以传入该类型或者其子类类型
1
2
3
4
5
6
7
8
9
10
11
12
Person<A> p1 = new Person<>(new A());
Person<A> p2 = new Person<>(new B());

//================
class A{}
class B extends A {}
class Person<E> {
E e;
public Person(E e) {
this.e = e;
}
}
  1. 泛型使用形式
1
2
List<Integer> list1 =new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<>();

[说明] 如果这样写 List list3 = new ArrayList(); 默认给它的泛型是[<E> E就是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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// List 自定义排序 匿名内部类
List<MyDate> as = new ArrayList<>();
as.sort(new Comparator<MyDate>() {
@Override
public int compare(MyDate o1, MyDate o2) {
return o1.getBirthday().compareTo(o2.getBirthday());
}
});

// 在MyDate类实现Comparable接口,重写compareTo方法 【封装】
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 int compareTo(MyDate o) {
int yearMinus = year - o.getYear();
if (yearMinus != 0) return yearMinus;

// 如果year相同就比较month
int monthMinus = month - o.getMonth();
if (monthMinus != 0) return monthMinus;

return day - o.getDay();
}

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

自定义泛型

泛型类

  • 基本语法
1
2
3
class 类名<T, R, M...> { // ...表示可以有多个泛型
成员
}
  • 注意细节
  1. 普通成员(属性、方法)可以使用泛型
  2. 使用泛型的数组, 不能初始化 T[] ts = new T[10]; 没有确定类型不知道在内存开辟多大空间
  3. 静态方法、静态属性中不能使用类的泛型,因为静态是和类相关的,在类加载时,对象还没创建,无法确定类型
  4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
  5. 如果在创建对象时, 没有指定类型, 默认为Object

泛型接口

  • 基本语法
1
2
3
interface 接口名<T, R…> {

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

泛型方法

  • 基本语法
1
2
3
修饰符 <T,R..> 返回类型 方法名(参数列表) {

}
  • 注意细节
  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat(E e) {}, 修饰符后没有 <T,R...> , eat方法不是泛型方法, 而是使用了泛型

泛型继承和通配符

  1. 泛型不具备继承性 List<Object> list = new ArrayList<String>(); x
  2. <?> : 支持任意泛型类型
  3. <? extends A>: 支持A类以及A类的子类, 规定了泛型的上限
  4. <? super A>: 支持A类以及A类的父类, 不限于直接父类,规定了泛型的下限

JUnit

  • 为什么需要JUnit
  1. 一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中
  2. 如果有多个功能代码测试,就需要来回注销,切换很麻烦
  3. JUnit 可以直接运行一个方法,并且可以给出相关信息, 方便很多
  • 基本介绍
  1. JUnit 是一个Java语言的单元测试框架
  2. 多数Java的开发环境都已经集成了 JUnit 作为单元测试的工具
1
2
3
4
5
6
import org.junit.jupiter.api.Test;

@Test
public void f() {
// ...
}

Java笔记——泛型
https://nessaj7.github.io/2022/06/27/90e5794c173e.html
作者
kuhn
发布于
2022年6月27日
许可协议