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

- 因为我们本应给
Printtable<String> printtable赋值public class StringPrinttable implements Printtable<String>,即为父类接口未持有匹配的子实现类
- 在上面的例子中,我们可以试着创建不匹配指定泛型的接口实现类对象,可以发现因为类型不匹配ide给我们标红报错了
泛型在方法中的应用
- 创建一个方法,如下方代码所示
java
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的对象,而对象中就已经指定了泛型的类型了
- 在这里不需要给
java
public static void main(String[] args){
//列表泛型方法测试用例是
ArrayList<Integer> list = new ArrayList<>();
list.add(9);
list.add(8);
list.add(7);
printList(list);
//这里String会被当作泛型T的值直接传入
}