Java 泛型 (Generic) 入门到精通:语法 + 原理 + 实战 + 避坑

一、为什么要用泛型?(不使用泛型有多痛)

在没有泛型之前,Java 集合只能用Object存储任意类型数据,强制类型转换随处可见,代码既不安全又难读,运行时才会报错!

1.1 反例:不使用泛型的代码

java 复制代码
public class GenericTest {
    public static void main(String[] args) {
        // 不使用泛型,默认存储 Object
        List list = new ArrayList();
        list.add("Java");
        list.add(123); 
        list.add(true);

        // 取值必须强转
        String str = (String) list.get(0);
        // 运行时报错!ClassCastException
        String num = (String) list.get(1); 
    }
}

痛点

  1. 类型不安全:可以随便放任意类型数据
  2. 强转繁琐:取值必须手动强转
  3. 报错延迟:编译不报错,运行才崩溃
  4. 可读性差:不知道集合里存的是什么

1.2 正例:使用泛型后

java 复制代码
public class GenericTest {
    public static void main(String[] args) {
        // 限定只能存 String
        List<String> list = new ArrayList<>();
        list.add("Java");
        // list.add(123); 编译直接报错!
        
        // 无需强转
        String str = list.get(0);
    }
}

泛型优势 :✅ 编译期类型检查 ,提前拦截错误✅ 自动类型推导 ,无需强制转换✅ 代码更通用 ,一套代码支持多种类型✅ 可读性更强,一眼知道数据类型


二、泛型基础语法(入门必看)

2.1 什么是泛型?

泛型 = 参数化类型 把数据类型当作参数传递,让类 / 接口 / 方法可以支持任意类型,同时保留类型安全。

2.2 泛型的三种使用场景

1)泛型类
java 复制代码
// T 是类型参数,可以随便写:T/E/K/V 都行
public class Box<T> {
    private T data;

    public void setData(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

// 使用
Box<String> box = new Box<>();
box.setData("泛型");
String data = box.getData();
2)泛型接口
java 复制代码
public interface Result<T> {
    T getData();
}

// 实现类指定类型
public class StringResult implements Result<String> {
    @Override
    public String getData() {
        return "success";
    }
}
3)泛型方法
java 复制代码
public class GenericMethod {
    // 泛型方法:<T> 声明泛型
    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }

    public static void main(String[] args) {
        String[] strs = {"A", "B"};
        String first = getFirst(strs);
    }
}

三、泛型通配符 ?(重点难点)

3.1 无界通配符 <?>

表示任意类型 ,常用于只读场景

java 复制代码
public static void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

⚠ 注意:<?> 不能添加数据(除了 null)。

3.2 上界通配符 <? extends 类>

只允许 本类 / 子类可读不可写

java 复制代码
// 只能接收 Number 及其子类:Integer、Double 等
public static void getNum(List<? extends Number> list) {
}

3.3 下界通配符 <? super 类>

只允许 本类 / 父类可写可读

java 复制代码
// 只能接收 Integer 及其父类:Number、Object
public static void addNum(List<? super Integer> list) {
    list.add(123);
}

3.4 通配符口诀(背会不踩坑)

  • <? extends T> 生产(读) 协变 → 只能取,不能存
  • <? super T> 消费(写) 逆变 → 可以存,可以取
  • <?> 任意类型 → 只能取,不能存

四、泛型核心原理:类型擦除(面试必考)

4.1 什么是类型擦除?

Java 泛型是伪泛型编译后泛型信息会全部消失,字节码中没有泛型!

java 复制代码
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 输出 true!泛型类型被擦除了
System.out.println(strList.getClass() == intList.getClass());

4.2 擦除规则

  1. 无限制 <T> → 擦为 Object
  2. 有限制 <? extends T> → 擦为 T

这就是泛型很多限制的根本原因!


五、泛型常见限制(必须记住)

  1. 不能使用基本类型int/long)→ 必须用包装类
  2. 不能创建泛型数组T[] arr = new T[10]
  3. 不能捕获泛型异常
  4. 静态方法不能用类泛型
  5. instanceof 不能判断泛型

六、企业级实战:泛型统一接口返回

这是后端开发最常用的泛型实战,统一返回结果,一套模板适配所有接口!

java 复制代码
// 泛型统一返回类
public class R<T> {
    private int code;
    private String msg;
    private T data;

    // 成功
    public static <T> R<T> ok(T data) {
        R<T> r = new R<>();
        r.code = 200;
        r.msg = "成功";
        r.data = data;
        return r;
    }

    // 失败
    public static <T> R<T> fail(String msg) {
        R<T> r = new R<>();
        r.code = 500;
        r.msg = msg;
        return r;
    }

    // getter/setter
}

// 接口使用
@RestController
public class UserController {
    @GetMapping("/user")
    public R<User> getUser() {
        User user = new User(1, "Java泛型");
        return R.ok(user);
    }
}

七、结合你给的枚举代码:泛型 + 枚举 最佳实践

你提供的枚举代码非常标准,我们可以用泛型做一个通用枚举工具类,让所有枚举都能复用!

7.1 定义通用枚举接口

java 复制代码
public interface BaseEnum {
    String getDesc();
}

7.2 你的枚举实现该接口

java 复制代码
public enum UserRole implements BaseEnum {
    ADMIN("管理员"),
    USER("普通用户");

    private final String desc;

    UserRole(String desc) {
        this.desc = desc;
    }

    @Override
    public String getDesc() {
        return desc;
    }
}

7.3 泛型工具类(所有枚举通用)

java 复制代码
public class EnumUtil {
    // 泛型方法:通用获取枚举描述
    public static <T extends BaseEnum> String getDesc(T t) {
        return t == null ? "" : t.getDesc();
    }
}

7.4 最终业务代码

java 复制代码
public class UserService {
    public void setUserRole(UserRole role) {
        String desc = EnumUtil.getDesc(role);
        System.out.println("当前角色:" + desc);

        if (UserRole.ADMIN == role) {
            System.out.println("执行管理员操作");
        } else {
            System.out.println("执行普通用户操作");
        }
    }

    public static void main(String[] args) {
        UserService service = new UserService();
        service.setUserRole(UserRole.ADMIN);
    }
}

输出

plaintext

java 复制代码
当前角色:管理员
执行管理员操作

八、面试高频题(背会稳过)

  1. Java 泛型的原理是什么? 答:类型擦除,编译后泛型信息消失,字节码无泛型,运行时都是原始类型。

  2. List<Object>List<?> 区别? 答:List<Object>可以添加任意对象;List<?>只能读不能写。

  3. extendssuper 区别? 答:extends 只读(生产者);super 可写(消费者)。

  4. **泛型有哪些限制?为什么?**答:因为类型擦除,不能用基本类型、不能创建泛型数组、不能判断泛型类型。


九、总结

  1. 泛型 = 类型安全 + 代码复用 + 自动推导
  2. 三大语法:泛型类、泛型接口、泛型方法
  3. 三大通配符:<?><? extends T><? super T>
  4. 核心原理:类型擦除
  5. 企业最爱用:统一返回泛型 R<T>
  6. 泛型 + 枚举 = 最优雅的业务常量方案
相关推荐
Andya_net33 分钟前
Java | Java内存模型JMM
java·开发语言
182******20831 小时前
2026年java后端还有机会吗?还能找到工作吗?
java·开发语言
XS0301061 小时前
Java基础 map集合
java·哈希算法·散列表
AIFQuant2 小时前
2026 全球股票/外汇/贵金属行情 API 深度对比:延迟、覆盖、价格与稳定性
python·websocket·ai·金融·mcp
凤山老林2 小时前
从0到1搭建企业级权限管理系统:Spring Boot + JWT + RBAC实战指南
java·spring boot·后端·权限管理·rbac
Ray Liang2 小时前
吐血整理JSON-RPC2.0的原理与应用
python
逍遥德2 小时前
AI时代,计算机专业大学生学习指南
java·javascript·人工智能·学习·ai编程
㳺三才人子2 小时前
簡單的 語音助手
python·ai编程·pip
Maiko Star2 小时前
让 AI 开口说话:Spring AI Alibaba 语音合成(TTS)实战
java·人工智能·spring·springai
计算机毕业编程指导师2 小时前
【计算机毕设推荐】Python+Hadoop+Spark共享单车数据可视化分析系统 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘
大数据·hadoop·python·计算机·数据挖掘·spark·课程设计