泛型是什么
- 泛型(Generics)是编程语言中的一种类型参数化机制,它允许在定义类、接口或方法时使用类型参数。这些类型参数可以在实际使用时被具体的类型所替换,从而实现代码的复用性和灵活性。通过泛型,可以编写更加通用的组件,同时保持类型安全。
引入
- 在实际的需求中,我们可能会遇见需要接收一个参数但是参数的类型不能确定,最简单的方法就是定义多个类型成员变量, 但随着接收参数的类型增加,去类中创建多个成员变量就会显得很麻烦,比如:当data变量可能为整型、浮点型、布尔型、字符型、字符串型的任何一种,总不能都去创建一个该类型的data成员变量吧,这时候就需要使用泛型了
泛型的应用
泛型在类中的应用
- 如下方代码所示,我们使用了
<T>给该类指定了一个需要接收的参数,这个参数实际上要接收的是一个类型值。这就是泛型参数,而后面的属性的类型,方法参数的类型就是由参数T来决定的:
package com.generic;
public class Box<T>{
private T data;
public void setData(T data) {
this.data = data;
}
public T getData() {
System.out.println("data类型是:" + this.data.getClass());
return data;
}
}
- 我们在GenericMain类中使用Box类,我们先指定Box的类型,在创建Box的对象,如下方代码所示:
public class GenericMain {
public static void main(String[] args){
Box <boxInter> = new Box<>();
boxInter.setData(9);
Box<String> boxString = new Box<>();
boxString.setData("Hello World");
int data = boxInter.getData();
System.out.println("data的值是:" + data);
String dataStr = boxString.getData();
System.out.println("data的值是" + dataStr);
}
}
- 拓展:实际上我们平时使用的ArrayList容器就是利用率泛型来成为不同类型的容器,即为先指定类型后创建与类型匹配的对象,可以按住
ctrl+右击查看ArrayList的源码,会发现实际上ArrayList的源码中也有一个泛型参数<E>
public class ArrayList<E> extends AbstractList<E>{ ...
泛型在接口的应用(多态--类型校验)
- 可以创建一个接口,该接口用于实现,根据不同的类型,进行不同的输出,如下方代码所示:
- 创建一个拥有泛型参数的接口,在接口中创建一个抽象方法
- 抽象方法中指定传入参数的类型由泛型决定
public interface Printtable<T> {
void print(T data);
}
- 创建实现接口的类
package com.generic;
public class IntegerPrinttable implements Printtable<Integer>{
@Override
public void print(Integer data) {
System.out.println("我被用于指定输出Interger类型的参数" + data);
}
}
package com.generic;
public class StringPrinttable implements Printtable<String>{
@Override
public void print(String data) {
System.out.println("我被用于指定输出String类型的参数" + data);
}
}
- 在Box类中使用接口
- 为了使用接口,可以先创建一个接口的引用
private Printtable<T> printtable;
- 创建一个使用该接口的方法printData(直接调用接口的引用拿到的对象中的print方法);
- 最后我们写一个Box的构造方法,形式参数为
Printtable<T> printtable,即为接口的引用,但需注意的是,这里的目的不是传入接口对象(接口本身也不能实例化为对象),而是利用向上转型(父类持有子类对象)持有实现类的对象,利用向上转型的特性,实现多态
public class Box<T>{
private T data;
private Printtable<T> printtable;
public Box(Printtable<T> printtable) {
this.printtable = printtable;
}
public void setData(T data) {
this.data = data;
}
public T getData() {
System.out.println("data类型是:" + this.data.getClass());
System.out.println(this.data);
return data;
}
public void printData() {
printtable.print(this.data);
}
}
- 在主类GenericMain中进行测试
- 在Box类中我们写了构造方法要求像传入一个接口实现类的对象,至于具体传入哪个则是根据Box对象选择了泛型的哪一种来决定的
- 之后就可以调用box中的方法了
- 我们分别调用
boxInter.printData(); boxString.printData();,printData会根据父类接口持有的类对象,匹配实现接口的子类的对象中的方法
public class GenericMain {
public static void main(String[] args){
Box<Integer> boxInter = new Box<>(new IntegerPrinttable());
boxInter.setData(9);
Box<String> boxString = new Box<>(new StringPrinttable());
boxString.setData("Hello World");
boxInter.printData();
boxString.printData();
}
}
- 泛型在接口中的应用的例子中,即体现了面向对象的多态特性,又体现了泛型能够进行类型校验的功能,在创建box对象的时候,使用的先指定类型,后创建与指定类型匹配的对象
- 在上面的例子中,我们可以试着创建不匹配指定泛型的接口实现类对象,可以发现因为类型不匹配ide给我们标红报错了

- 因为我们本应给
Printtable<String> printtable赋值public class StringPrinttable implements Printtable<String>,即为父类接口未持有匹配的子实现类

泛型在方法中的应用
public static<T> void printList(ArrayList<T> list){
for(T data:list){
System.out.println(data);
}
}
- 调用方法,如下方代码所示
- 在这里不需要给
public static<T> void printList(ArrayList<T> list)传单独的类型给泛型,因为在printList方法的原型中只要求传入ArrayList的对象,而对象中就已经指定了泛型的类型了
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>();
list.add(9);
list.add(8);
list.add(7);
printList(list);
}