一,什么是泛型

泛型(Generics) 是 Java SE 5 引入的重要特性,它允许你在定义类、接口和方法时使用“类型参数”。简单来说,泛型就是把类型当作参数传递。它的核心目的是实现类型安全和代码复用,让你在编译阶段就能发现类型不匹配的问题,而不是等到程序运行并崩溃时才发现

泛型的本质是参数化类型。也就是说,你可以在定义类、接口或方法时,把“具体的类型”变成一个可以被替换的“变量”。

二,泛型的作用

1,泛型的作用
 

  • 类型安全 (Type Safety):把错误留在编译期。如果你试图往 List<String> 里放一个整数,编译器会立刻报错,而不是等程序运行时才崩溃。

  • 消除强制转换 (Eliminate Casting):代码更简洁。不需要再写 (String) list.get(0) 这种啰嗦的代码了,编译器知道它就是 String。

  • 代码复用 (Reusability):一套逻辑,百种类型。你可以写一个通用的“排序”或“存储”逻辑,然后复用到 Integer、String 等不同类型上。

泛型可以应用于类、接口和方法。常见的占位符包括 T (Type), E (Element), K (Key), V (Value)。

三,泛型的种类

    了解泛型首先了解泛型的结构----通配符

泛型的通配符

语法 术语 形象描述
<?> 无界通配符 “随便什么都行”。只读,不能往里写数据。
<? extends T> 上界通配符 “T 及其子类”。比如 <? extends Number> 接受 Integer、Double。通常用于“读取”。
<? super T> 下界通配符 “T 及其父类”。通常用于“写入”。

泛型的语法

顺序 专业名称 语法特征 核心作用
01 泛型接口 interface Name<T> 定义一套通用的行为标准。
02 受限类型泛型类 class Name<T extends Number> 限制类型范围,确保可以使用特定父类的方法。
03 泛型方法 <E> void method(E e) 独立于类之外的类型灵活性,静态方法必备。
04 上界通配符 <? extends T> 安全读取:允许 T 及其子类。
05 下界通配符 <? super T> 安全写入:允许 T 及其父类。

1,为什么需要泛型

在没有泛型之前,程序员只能用 Object 来代表一切,但这会带来两个大麻烦:

类型转换很累:每次拿出来都要强转 (String) list.get(0)

容易崩溃:如果不小心往装 String 的集合里塞了个 Integer,编译时不报错,一运行就抛出

ClassCastException

   // 1. 无泛型的List:默认存储Object类型,能装任意数据
        List list = new ArrayList();
        
        // 2. 往“本意存字符串”的集合里塞了整数,编译完全不报错(坑1)
        list.add("Java");
        list.add("Python");
        list.add(123); // 错误插入Integer,但编译器不提示
        
        // 3. 取值时必须强制类型转换,代码冗余且麻烦(坑2)
        String str1 = (String) list.get(0); // 正常
        String str2 = (String) list.get(1); // 正常
        // 4. 运行时崩溃:Integer转String,抛出ClassCastException
        String str3 = (String) list.get(2); 
 // 1. 声明泛型List<String>:明确只能存字符串
        List<String> list = new ArrayList<>();
        
        // 2. 编译期就拦截错误:往String集合塞Integer,直接报错(解决“容易崩溃”)
        list.add("Java");
        list.add("Python");
        // list.add(123); // 编译器直接标红:不兼容的类型,无法编译
        
        // 3. 取值时无需强制转换,直接拿到String(解决“类型转换累”)
        String str1 = list.get(0);
        String str2 = list.get(1);
        System.out.println(str1 + ", " + str2); // 输出:Java, Python
 

2,理解什么是泛型,泛型的方法

import java.util.ArrayList;
import java.util.List;

// =================【01:泛型接口 (Generic Interface)】=================
// 解析:T 是一个占位符,代表“某种类型”。接口定义了处理这种类型的标准。
interface IProcessor<T> {
    void process(T item);
}

// =================【02:受限类型泛型类 (Bounded Type Class)】=================
// 解析:<T extends Number> 规定 T 必须是 Number 或其子类(如 Integer, Double)。
// 这样在类内部就可以安全地调用 Number 的方法,如 doubleValue()。
class Calculator<T extends Number> implements IProcessor<T> {
    
    @Override
    public void process(T item) {
        // 因为有限制,编译器知道 item 一定有数字特征
        System.out.println(">>> 正在处理数字: " + item.doubleValue());
    }

    // =================【03:泛型方法 (Generic Method)】=================
    // 解析:这里的 <E> 与类的 <T> 无关。它允许该方法接收任何类型的参数。
    // 即使类被限制为数字,这个方法依然可以处理 String。
    public <E> void log(E info) {
        System.out.println("[系统日志]: " + info.toString());
    }
}

// =================【04 & 05:通配符 (Wildcards - PECS原则)】=================
class Warehouse {
    
    // 【04:上界通配符 ? extends T】
    // 解析:表示“某种 Number 的子类”。
    // 场景:适合【读取】。因为不管是 Integer 还是 Double,它们都是 Number。
    public static void readData(List<? extends Number> list) {
        for (Number n : list) {
            System.out.println("读取内容: " + n);
        }
        // list.add(10); // 禁止写入!编译器无法确定具体是哪种数字子类
    }

    // 【05:下界通配符 ? super T】
    // 解析:表示“某种 Integer 的父类”。
    // 场景:适合【写入】。因为 Integer 一定可以放入 Integer、Number 或 Object 的容器。
    public static void writeData(List<? super Integer> list) {
        list.add(999); // 允许写入 Integer
        System.out.println("写入成功!");
    }
}

public class GenericsHero {
    public static void main(String[] args) {
        // 实例化泛型类
        Calculator<Double> myCalc = new Calculator<>();
        myCalc.process(3.14);
        myCalc.log("测试运行中..."); // 泛型方法调用

        // 通配符测试
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        Warehouse.readData(intList);   // 向上转型读取
        Warehouse.writeData(intList);  // 向下转型写入
    }
}

 泛型方法为什么要单独写 <E>

静态方法(static)无法访问类定义的泛型 T,因为静态成员属于类,而 T 只有在 new 对象时才确定。所以静态方法如果要用泛型,必须自己声明 <E>

3,泛型的好处

特性 不用泛型 (使用 Object) 使用泛型
类型安全 编译时不报错,运行时可能崩 (ClassCastException) 编译时检查,写错类型代码直接报红
代码简洁 拿数据时必须手动强转:(String)box.get() 自动识别类型,代码更干净
可读性 看到 List 不知道里面装的是什么 看到 List<User> 一眼就知道装的是用户

四,泛型错误用法

基本类型

 List<int>List<Integer>(泛型必须是对象类型)。

异常类

 class MyException<T> extends Exception(异常不能带泛型)

实例化

T obj = new T();(因为类型擦除,运行时 JVM 根本不知道 T 是什么,所以无法 new)。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐