什么是泛型
泛型是一种模板,用来解决不同类处理相同逻辑时进行类型传参。
在ArrayList初始化时,实际是用一个Object数组来动态分配数据的大小。
public class ArrayList {
private Object[] array;
private int size;
public void add(Object e) {...}
public void remove(int index) {...}
public Object get(int index) {...}
}若在创建数据的时候使用Object 对象来实现,获取数据内的对象需要进行类型转换:
ArrayList<Object> objects = new ArrayList<>();
objects.add("hello");
objects.add(1);
String s1 = (String) objects.get(1);而进行类型转换过程中,如果数组内的数据类型与转换类型不一致会抛出类转换异常ClassCastException ,如果需要解决此类问题可以单独去根据每个类型去单独写一个数组类 StringArrayList、DoubleArrayList、... 较为繁琐。
而泛型正是为了决绝此类问题,提供的一个模板功能。可以将不同数据类型作为参数进行自动转换,由编译器自动识别是否存在类转换问题。ArrayList中使用泛型:
public class ArrayList<E> {
private E[] array;
private int size;
public void add(E e) {...}
public void remove(int index) {...}
public E get(int index) {...}
}在进行初始化的时候将数组类型传入:
ArrayList<String> strings = new ArrayList<>();
strings.add("hello");
strings.add(123); //编译错误
String s2 = strings.get(1);//不需要类型转换使用泛型
泛型类
定义一个可以存储任意类数据类型的容器。
public class GenericClassOriDemo {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
//不适用泛型,使用类转换容易出错
GenericClassOriDemo oriDemo = new GenericClassOriDemo();
oriDemo.setValue("Hello");
String value = (String) oriDemo.getValue();
oriDemo.setValue(10);
String value1 = (String) oriDemo.getValue();//运行是出错,类转换异常优化为泛型类:
public class GenericClassOriDemo1<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
//优化为泛型类后
GenericClassOriDemo1<String> oriDemo1 = new GenericClassOriDemo1<>();
oriDemo1.setValue(123);//编译时提示错误;
oriDemo1.setValue("world");
String value2 = oriDemo1.getValue();//无需进行手动转换泛型方法
定义一个方法可以打印多种类型的输入参数
//优化前
public class GenericMethodOriDemo {
public static void printArray(int[] array)
{
for (int i : array) {
System.out.println(i);
}
}
}
//优化后
public class GenericMethodOptimizeDemo {
public static <T> void printArray(T[] array)
{
for (T i : array) {
System.out.println(i);
}
}
}有界类型参数
可以对泛型传入参数类型进行限制。
定义一个计算数组总和的方法,仅支持数值类型:
public static <T extends Number> double sum(T[] array)
{
double sum = 0;
for (T i : array) {
sum += i.doubleValue();
}
return sum;
}通配符
使用
?表示未知类型,结合extends和super增加灵活性。
//通配符
public static void printArray(List<? extends Number> list){
for (Number i : list) {
System.out.println(i);
}
}
public static void addSum(List<? super Integer> list){
for (int i = 1; i <= 3; i++) {
list.add(i); // 可以安全添加 Integer 到父类集合
}
}注意事项
- 类型擦除: 泛型在编译器,编译器会自动将类型转换为Object或其上界类型。
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 运行时两者类型相同(都是 List),因为类型参数被擦除
System.out.println(stringList.getClass() == intList.getClass()); // 输出:true- 避免原始类型: 使用原始类型(如
List而非List<String>)会退化为Object类型,失去泛型的类型安全检查,编译期会报警告。 - 优先使用接口类型: 声明泛型变量时,优先使用接口(如
List<String>而非ArrayList<String>),提高代码灵活性。 - 泛型类的继承: 子类继承泛型父类时,可指定具体类型参数或继续参数化:
// 父类:泛型类
class Parent<T> { /* ... */ }
// 子类1:指定具体类型参数(非泛型类)
class Child1 extends Parent<String> { /* ... */ }
// 子类2:继续参数化(泛型类)
class Child2<T> extends Parent<T> { /* ... */ }