一、什么是泛型
定义类、接口、方法时;同时声明了一个或多个类型变量(如:
public class ArrayList
......
}
泛型的本质,是把具体的数据类型作为参数传给类型变量。
泛型的作用:提供了在编译阶段所能操作的数据类型,并自动进行检查的能力。这样可以避免强制类型转换,及可能出现的异常。
例子
package org.example.general;
import java.util.ArrayList;
public class GenExample1 {
public static void main(String[] args) { // ArrayList 本身是一个泛型类,在实例化的时候没有指定泛型类型,那么默认是Object类型
ArrayList arr = new ArrayList();
arr.add("abc");
arr.add(123);
arr.add(true);
for(int i = 0;i < arr.size();i++){
String temp = (String) arr.get(i);
System.out.println(temp);
}
}
}
运行结果:
abc
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at org.example.general.GenExample1.main(GenExample1.java:13)
可以看到获取第二个数据的时候,报了类型转换异常。
我们优化一下代码,给ArrayList定义变量的时候加上泛型标识,如下:
可以发现,在编译阶段就已经报错了。因此后面我们添加元素的时候,就只能添加String类型,这样就规范了值的传入,也就能避免后面的类型转换异常了。
二、泛型类
定义泛型类
修饰符 class 类名 <类型变量,类型变量,...> {
......
}
注意,类型变量建议用大写英文字母,常用的有:E,T,K,V等。
例如,当我们定义类的时候,类中有变量我们不确定它的类型,我们就可以定义泛型类,如下:
package org.example.general;
public class Student
private T name; // 在这里使用泛型类型 T
private K age; // 在这里使用泛型类型 K
public Student(){};
public Student(T name,K age){
this.name = name;
this.age = age;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public K getAge() {
return age;
}
public void setAge(K age) {
this.age = age;
}
}
在我们创建对象的时候,来确定泛型类的数据类型,如下:
package org.example.general;
public class GenExample2 {
public static void main(String[] args) {
Student
System.out.println(stu1.getName());
}
}
再看例子二:
package org.example.general;
public class GenExample3{
public static void main(String[] args) {
People
per1.setHobby(new Hdance());
per1.getHobby();
People
per2.setHobby(new Hsing());
per2.getHobby();
}
}
class People
private T hobby;
public void setHobby(T hobby) {
this.hobby = hobby;
}
public void getHobby(){
System.out.println(hobby.toString());
}
}
class Hsing{
@Override
public String toString() {
return "唱歌";
}
}
class Hdance{
@Override
public String toString() {
return "跳舞";
}
}
三、泛型接口
定义泛型接口
修饰符 interface 接口名<类型变量,类型变量...>{
......
}
什么时候用到了泛型接口:接口当中,方法的参数类型或方法的返回值类型确定不了,这个时候就可以使用泛型接口。
例如该场景:
系统需要处理学生和老师的数据需要提供两个功能:1、保存对象数据2、根据名称查询数据
代码如下:
定义学生类:
package org.example.generalinterface;
public class Student {
}
定义老师类:
package org.example.generalinterface;
public class Teacher {
}
定义操作类:
package org.example.generalinterface;
public interface Operator
public abstract void saveData(E obj);
public abstract E getData();
}
// 接口中的泛型在什么时候被确定
// 方式一:实现类实现泛型接口,没有指定具体的泛型类型,那么默认是object类型
class OperatorImpl1 implements Operator{
@Override
public void saveData(Object obj) {
}
@Override
public Object getData() {
return null;
}
}
// 方式二:实现类实现接口时,直接指定接口中泛型的类型(使用最多的方式)
class OperatorImpl2 implements Operator
@Override
public void saveData(Teacher obj) {
}
@Override
public Teacher getData() {
return null;
}
}
// 方式三:实现类实现了泛型接口,泛型没有指定,那么泛型在实现类实例化的时候被确定
class OperatorImpl3
@Override
public void saveData(E obj) {
}
@Override
public E getData() {
return null;
}
}
四、泛型方法
定义泛型方法
修饰符 <类型变量,类型变量...> 返回值类型 方法名(形参列表里){
......
}
例如
public static
}
在调用方法的时候确定泛型类型。
什么时候使用泛型方法:当我们定义方法的时候参数类型或返回值类型不确定的时候,就可以使用泛型方法,当我们调用泛型方法的时候来确定泛型方法。例如:
ackage org.example.generalMethod;
public class GenExample1 {
public static void main(String[] args) {
GenExample1.show("abc"); // 输出string
GenExample1.show(123); // 输出integer
} // 第一个
public static
if(t instanceof String){
System.out.println("string");
} else if (t instanceof Integer) {
System.out.println("interger");
}
}
}
通配符
?号,可以在使用泛型的时候代表一切类型,E T K V 是在定义泛型的时候使用。这个通配符一般会结合集合去用。
package org.example.generalMethod;
import java.util.ArrayList;
public class GenExample2 {
public static void main(String[] args) {
ArrayList
ArrayList
method(str);
method(inte);
} // 通配符不需要定义泛型,就可以使用泛型
public static void method(ArrayList> arr){
}
}
泛型的上下限
泛型上限: ? extends Car,?能接收的必须是Car或者他的子类
泛型下限: ? super Car,?能接收的必须是Car或者其父类。
可以看到超过上限,编译的时候就报错了。