说说对泛型的理解?

在Java开发过程中,泛型(Generics) 是一个非常基础且重要的语法特性。它不仅提升了代码的复用性与类型安全,还大大增强了集合、工具类等容器的灵活性。本文将结合理论和详细代码示例,从泛型的原理、常见用法、PECS原则等方面,系统讲解什么是Java泛型,以及如何正确高效地使用泛型。


一、什么是泛型

泛型,顾名思义,就是“参数化类型”。通俗的说,泛型允许在定义类、接口、方法时,将类型作为参数进行传递,使代码可以应用于多种不同的类型。

本质作用:

  • 让同一份代码适配不同类型的数据
  • 在编译期提供类型检查,避免类型转换异常

例子:没有泛型的集合(JDK1.5之前)

List list = new ArrayList();
list.add("string");
list.add(123); // 没有任何报错
// 取出元素需要强转
String str = (String) list.get(0); // 正常
String str2 = (String) list.get(1); // 运行时报ClassCastException

例子:有泛型的集合

List<String> list = new ArrayList<>();
list.add("string");
// list.add(123); // 编译报错!
String str = list.get(0); // 不需要强转,类型安全

二、泛型的基本使用

1. 泛型类

public class Box<T> {
    private T value;
    public void setValue(T value) { this.value = value; }
    public T getValue() { return value; }
}

public static void main(String[] args) {
    Box<Integer> intBox = new Box<>();
    intBox.setValue(123);
    int i = intBox.getValue(); // 自动类型检查,无需强转

    Box<String> strBox = new Box<>();
    strBox.setValue("hello");
    String s = strBox.getValue(); // 类型安全
}

2. 泛型方法

public class Utils {
    // 泛型方法声明在返回值前加 <T>
    public static <T> void printArray(T[] array) {
        for (T t : array)
            System.out.println(t);
    }
}

public static void main(String[] args) {
    Integer[] nums = {1, 2, 3};
    String[] strs = {"a", "b"};
    Utils.printArray(nums); // 自动识别T为Integer
    Utils.printArray(strs); // 自动识别T为String
}

3. 泛型接口

public interface Converter<F, T> {
    T convert(F from);
}

public class StringToIntegerConverter implements Converter<String, Integer> {
    @Override
    public Integer convert(String from) {
        return Integer.valueOf(from);
    }
}

三、通配符 <?> 详解

1. 基础通配符

<?> 代表未知类型。例如,可以接收任意类型的List:

public static void printList(List<?> list) {
    for(Object o : list) System.out.println(o);
}

2. 上界通配符(extends)

<? extends Number> 代表某种Number的子类型,常用于只读(取数据)场景。

public static void sumList(List<? extends Number> numbers) {
    double sum = 0;
    for (Number n : numbers) sum += n.doubleValue();
    System.out.println("Sum: " + sum);
}

3. 下界通配符(super)

<? super Integer> 代表Integer或其父类类型,常用于只写(存数据)场景。

public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    // 不能读取到具体类型(只能当Object用)
}

口诀:

  • Producer Extends(PE) —— 只从集合取数据用 extends
  • Consumer Super(CS) —— 只向集合存数据用 super

四、泛型常见应用和典型代码

1. 类型擦除现象

Java的泛型是“伪泛型”,编译后会进行类型擦除、转为原始类型。例如:

List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

System.out.println(stringList.getClass() == intList.getClass()); // true

意味着“泛型信息只存在于编译期,运行时被擦除”。

2. 泛型不能用于静态变量

public class Demo<T> {
    // static T field; // 编译不通过,静态变量无法使用泛型类型
}

3. 数组与泛型

// T[] arr = new T[10]; // 错误,不能直接创建泛型数组
Object[] arr = new Integer[10]; // 可行,但存在类型转型风险

五、实战代码解析:泛型工具类与PECS用法

以集合复制为例,演示PECS原则:

// 只读 - 泛型上界
public static double sum(List<? extends Number> list) {
    double result = 0;
    for (Number n : list) result += n.doubleValue();
    return result;
}
// 只写 - 泛型下界
public static <T> void copy(List<? super T> dest, List<T> src) {
    for (T t : src)
        dest.add(t);
}

public static void main(String[] args) {
    List<Integer> src = Arrays.asList(1, 2, 3);
    List<Number> dest = new ArrayList<>();
    copy(dest, src); // dest可以是Number,可以是Object...

    System.out.println(sum(dest));
}

六、泛型的好处与注意事项

优点:

  • 类型安全,编译阶段预防类型错误
  • 代码复用,同一结构适配多种类型
  • 可读性强,类型清晰可见、减少强解

注意事项:

  • 泛型类型在运行时会被擦除(无实例化类型),不能用==instanceof判断泛型参数类型
  • 泛型类不能直接使用基本类型(如Box<int>会报错,只能用Box<Integer>
  • 静态上下文(static变量和方法)不能引用类的泛型参数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值