Java—— 泛型

泛型的引入

对于不加泛型的集合

  1. 不能对加入到ArrayList中的数据类型进行约束

    1
    2
    3
    4
    5
    ArrayList arraylist = new ArrayList();
    arraylist.add(new Dog("dahuang",3));
    arraylist.add(new Cat("tom",2));
    //arraylist中可以添加任意类型
    //如果要对arraylist进行遍历输出Dog信息时,Cat就会报错
  2. 遍历时,需要进行类型转换(Object—>具体类),如果集合中数据量比较大,对效率有影响

    1
    2
    3
    4
    5
    6
    Iterator iterator = arrayList.iterator();
    while (iterator.hasNext()) {
    Object next = iterator.next();
    Dog dog = (Dog) next; //向下转型
    System.out.println(dog.getName() + dog.getAge());
    }

加入泛型

1
ArrayList <Dog> arraylist = new ArrayList<Dog>();

这样arraylist中只能存放Dog类数据

好处:编译时,检查添加元素的类型,提高了安全性,减少类型转换的次数,提高效率。

泛型概念及作用

  1. 泛型是一种在编译时提供更强类型检查的特性,它使得你可以在设计和使用类、接口和方法时参数化类型。

  2. 在类声明或实例化时,只要指定好需要的的具体类型即可。

  3. 泛型可以在类声明时通过一个标识表示类中某个属性的类型,或者某个方法返回值类型,或参数类型

    1
    2
    3
    4
    5
    6
    7
    public class Box<T> {
    // ...
    }
    public <T> T genericMethod(T parameter) {
    // ...
    }

  4. 类型安全:泛型可以提供在编译时检查类型的机制,避免了运行时出现类型错误,提高安全性。

  5. 代码重用:泛型允许编写用于多种类型的的代码,而不必重复编写,提高代码重用性和可维护性。

  6. 集合框架:集合框架使用泛型,可以使得在集合中存储元素和检索元素更加安全,并且无需进行显示的类型转换。

泛型语法

泛型的声明:

interface 接口名 {…}

class 类名 <k,v…>{…}

泛型的实例化:

要在类名后面指定类型参数的值,如:

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

使用细节:

  1. 泛型K,T,V…只能是引用类型,不能是基本数据类型。

  2. 在给泛型指定具体类型后,可以传入该类型或者其子类型。

    1
    2
    3
    4
    5
    Pig<A> aPig = new Pig<A>(new A());
    Pig<A> aPig = new Pig<A>(new B());
    class A{}
    class B extends A{}
    class Pig<T>{}
  3. 泛型的使用形式:

    1
    2
    3
    4
    5
    6
    //通常将后面的省略
    ArrayList<Integer> list1 = new ArrayList <>();
    List<Integer> list2 = new ArrayList <>();
    //不推荐
    ArrayList<Integer> list3 = new ArrayList <Integer>();
    List<Integer> list4 = new ArrayList <Integer>();
  4. 如果没给泛型指定具体对象,则默认为Object

    1
    2
    3
    ArrayList arraylist = new ArrayList();
    //等价于
    ArrayList <Object> arraylist = new ArrayList<>();

自定义泛型

自定义泛型类

基本语法:class 类名<T,R…>{…}

使用细节:

  1. 普通成员可以使用泛型(属性、方法)

  2. 使用泛型的数组不能对其进行初始化(数组需要根据数据类型分配空间)

  3. 静态方法中不能使用类的泛型(静态方法和属性,在类加载的适合就被加载和初始化,而不是在类实例化时)

  4. 泛型类的类型是在创建对象时确定的(因为创建对象时需要指定确定类型)

  5. 如果创建时没有指定类型,那就默认为Object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class CustomGenerics {
    public static void main(String[] args) {
    Student<Integer> student1 = new Student<>(001);
    Student student2 = new Student(); //未指定类型则为Object
    student2.setId(002);
    student2.setId("002");
    }
    }
    class Student<E>{ //泛型类
    E id; //属性值泛型

    public Student(){}

    public Student(E id) { //方法泛型
    this.id = id;
    }

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

自定义泛型接口

基本语法:interface 接口名 <T,R…>{…}

使用细节:

  1. 在接口中,静态成员变量也不能使用泛型
  2. 接口中的所有属性都默认为static,所以接口中属性不能用泛型
  3. 泛型接口的类型在继承接口实现接口时确定
  4. 如果没有指定具体类型,则默认为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
interface AA<T,R>{
T get(R r);
void hi(T t1, R r1);
default T method(R r){
return null;
}
}

interface BB<E> extends AA<Integer,String>{ //继承接口
void run (E e);
}
class CC implements BB<String>{ //实现接口


@Override
public Integer get(String s) {
return null;
}

@Override
public void hi(Integer t1, String r1) {

}

@Override
public Integer method(String s) {
return BB.super.method(s);
}

@Override
public void run(String s) {

}
}

自定义泛型方法

基本语法:修饰符 <T,R…>返回类型 方法名(参数列表){…}

使用细节:

  1. 泛型方法可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat (E e){},修饰符后没有<T,R…> eat方法不是泛型方法,只是使用了泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CustomMethodGenerics {
public static void main(String[] args) {
Car car = new Car();
car.fly("BMW",100); //当调用方法时,传入参数,编译器会根据参数来确定类型
Fish<ArrayList<String>, Integer> fish = new Fish<>();
fish.eat("虾米",10);
}
}
class Car{ //普通类
public void run(){} //普通方法
public <T,R> void fly(T t,R r){} //泛型方法
}

class Fish<T,R>{ //泛型类
public void run(){} //普通方法
public <U,M> void eat(U u, M m){} //泛型方法
}

泛型的继承和通配符

  1. 泛型不具备继承性

    1
    2
    Object aa = new String("xx");   //true
    List<Object> list = new ArrayList<String>(); //false
  2. :支持任意泛型类型
  3. :支持A类以及A类的子类,规定了泛型的上限(AA)
  4. :支持A类以及A类的父类,不限于直接父类,规定了泛型的下限(AA)
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
public class GenericExtends {
public static void main(String[] args) {
//举例说明下面三个方法的使用
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AA> list3 = new ArrayList<>();
List<BB> list4 = new ArrayList<>();
List<CC> list5 = new ArrayList<>();
//如果是 List<?> c , 可以接受任意的泛型类型
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
//List<? extends AA> c: 表示 上限, 可以接受 AA 或者 AA 子类
// printCollection2(list1);//×
// printCollection2(list2);//×
printCollection2(list3);//√
printCollection2(list4);//√
printCollection2(list5);//√
//List<? super AA> c: 支持 AA 类以及 AA 类的父类, 不限于直接父类
printCollection3(list1);//√
//printCollection3(list2);//×
printCollection3(list3);//√
//printCollection3(list4);//×
//printCollection3(list5);//×
//? extends AA 表示 上限, 可以接受 AA 或者 AA 子类

}
public static void printCollection2 (List < ? extends AA > c){
for (Object object : c) {
System.out.println(object);
}
}
//List<?> 表示 任意的泛型类型都可以接受
public static void printCollection1 (List < ? > c){
for (Object object : c) { // 通配符, 取出时, 就是 Object
System.out.println(object);
}
}
//? super 子类类名 AA:支持 AA 类以及 AA 类的父类, 不限于直接父类,规定了泛型的下限
public static void printCollection3 (List < ? super AA > c){
for (Object object : c) {
System.out.println(object);
}
}
class AA { }
class BB extends AA { }
class CC extends BB { }
}