news 2026/2/20 8:14:20

手把手教会你什么是 Java 泛型 —— 从“报错崩溃”到“秒懂原理”(Spring Boot 实战)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教会你什么是 Java 泛型 —— 从“报错崩溃”到“秒懂原理”(Spring Boot 实战)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)


一、真实痛点:没有泛型的世界有多痛苦?

想象一下你在开发一个电商系统,需要写一个通用的订单缓存工具类

// 没有泛型的写法(Java 5 之前) public class OrderCache { private List orders = new ArrayList(); // 存什么?String?Order?User? public void add(Object order) { orders.add(order); } public Object get(int index) { return orders.get(index); } }

使用时:

OrderCache cache = new OrderCache(); cache.add("这不是订单!"); // 编译器不报错! cache.add(new User()); // 也能加! Order order = (Order) cache.get(0); // 运行时 ClassCastException!

💥问题来了

  • 编译期无法检查类型安全;
  • 强转容易出错;
  • 代码可读性差,别人根本不知道这个 list 到底存的是啥!

这就是泛型要解决的核心问题:在编译期就保证类型安全!


二、什么是泛型?一句话讲清楚

泛型(Generics)就是“参数化类型”——把类型当作参数传进去,让同一个类/方法能安全地处理多种数据类型。

就像你买衣服,不是固定 S/M/L,而是“按你的尺码定制”。


三、手把手实战:用 Spring Boot 写一个泛型工具类

场景:我们要做一个通用的响应封装类,既能返回用户信息,也能返回订单列表。

✅ 正确用法:使用泛型
// 通用响应体 public class ApiResponse<T> { private int code; private String message; private T data; // T 是泛型参数,代表任意类型 public static <T> ApiResponse<T> success(T data) { ApiResponse<T> response = new ApiResponse<>(); response.code = 200; response.message = "success"; response.data = data; return response; } public static <T> ApiResponse<T> error(String msg) { ApiResponse<T> response = new ApiResponse<>(); response.code = 500; response.message = msg; return response; } // getter/setter 略 }
Controller 中使用:
@RestController public class UserController { @GetMapping("/user") public ApiResponse<User> getUser() { User user = new User(1L, "张三"); return ApiResponse.success(user); // 自动推断 T 为 User } @GetMapping("/orders") public ApiResponse<List<Order>> getOrders() { List<Order> orders = Arrays.asList( new Order(1L, "U1001", new BigDecimal("99.9")) ); return ApiResponse.success(orders); // T 为 List<Order> } }

优势

  • 编译器知道dataUserList<Order>
  • 不需要强转;
  • IDE 自动提示字段,开发效率翻倍!

四、泛型的三种常见形式

1. 泛型类(Generic Class)

public class Box<T> { private T content; public void set(T content) { this.content = content; } public T get() { return content; } } // 使用 Box<String> stringBox = new Box<>(); stringBox.set("Hello"); String s = stringBox.get(); // 直接是 String,无需强转!

2. 泛型方法(Generic Method)

public class Utils { // 方法级别的泛型,与类无关 public static <T> void printList(List<T> list) { for (T item : list) { System.out.println(item); } } } // 调用 Utils.printList(Arrays.asList("A", "B")); // T = String Utils.printList(Arrays.asList(1, 2, 3)); // T = Integer

3. 泛型接口(Generic Interface)

public interface Repository<T> { void save(T entity); T findById(Long id); } @Service public class OrderRepository implements Repository<Order> { @Override public void save(Order order) { /* ... */ } @Override public Order findById(Long id) { /* ... */ } }

💡 Spring Data JPA 的JpaRepository<T, ID>就是典型泛型接口!


五、反例警告:这些错误你一定犯过!

❌ 反例 1:用Object代替泛型

// 错误示范 public class BadCache { private List data = new ArrayList(); // raw type(原始类型) public void add(Object obj) { data.add(obj); } public Object get(int i) { return data.get(i); } }

⚠️ 编译器会警告:Raw use of parameterized class 'List'
而且完全丧失类型安全!


❌ 反例 2:泛型中使用基本类型

List<int> numbers = new ArrayList<>(); // ❌ 编译错误!

✅ 正确写法:用包装类

List<Integer> numbers = new ArrayList<>(); // ✅

❌ 反例 3:运行时想获取泛型类型(经典误区!)

public class GenericDemo<T> { public void printType() { // 下面这行会报错! System.out.println(T.class); // ❌ 不合法! } }

📌重要知识:Java 泛型是编译期特性,运行时会被“擦除”(Type Erasure)!
所以List<String>List<Integer>在运行时都是List

✅ 如果真需要运行时类型,可通过传递 Class 对象解决:

public class TypedCache<T> { private Class<T> type; public TypedCache(Class<T> type) { this.type = type; } public T createInstance() throws Exception { return type.getDeclaredConstructor().newInstance(); } } // 使用 TypedCache<User> userCache = new TypedCache<>(User.class); User user = userCache.createInstance(); // 安全创建实例

六、高级技巧:泛型通配符(?)—— 灵活又安全

场景:写一个方法,能打印任何类型的列表

✅ 用上限通配符? extends Number
public void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n.doubleValue()); } } // 可以传入 printNumbers(Arrays.asList(1, 2, 3)); // List<Integer> printNumbers(Arrays.asList(1.5, 2.7)); // List<Double>
✅ 用下限通配符? super Integer
public void addIntegers(List<? super Integer> list) { list.add(100); // 可以添加 Integer list.add(200); } // 可以传入 List<Number> nums = new ArrayList<>(); addIntegers(nums); // ✅ 因为 Number 是 Integer 的父类

🧠记忆口诀

  • Producer extends(生产者用extends,只读)
  • Consumer super(消费者用super,只写)

七、Spring Boot 中泛型的典型应用

1. RestTemplate 返回泛型结果

ResponseEntity<ApiResponse<User>> response = restTemplate.exchange( url, HttpMethod.GET, null, new ParameterizedTypeReference<ApiResponse<User>>() {} // 匿名子类保留泛型信息 );

2. 自定义异常处理器

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity<ApiResponse<Void>> handleBusiness(BusinessException e) { return ResponseEntity.badRequest() .body(ApiResponse.error(e.getMessage())); } }

八、总结:泛型核心价值

问题没有泛型有泛型
类型安全运行时才报错编译期就拦截
代码可读性不知道存的是啥一看就知道是List<Order>
强转风险需要(Order) obj零强转
复用性每个类型写一套一套代码通吃所有类型

记住:泛型不是“炫技”,而是写出健壮、可维护代码的基本功


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/18 8:23:58

计算机Java毕设实战-基于SpringBoot+Vue的社区便民服务平台邻里纠纷调解基于SpringBoot的社区邻里服务平台设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/2/14 17:51:15

互联网大厂Java求职面试实战:Spring Boot、微服务与AI技术栈详解

互联网大厂Java求职面试实战&#xff1a;Spring Boot、微服务与AI技术栈详解 面试场景背景 本次面试设定于在线教育场景&#xff0c;面试官严肃专业&#xff0c;求职者谢飞机是个搞笑但基础还行的程序员。涵盖Java SE、Spring Boot、微服务、AI技术栈等。第一轮提问&#xff1a…

作者头像 李华
网站建设 2026/2/4 18:00:16

如何解决React组件props传值只读属性修改导致的报错问题

你想解决React开发中直接修改组件props报TypeError: Cannot assign to read only property的核心错误&#xff0c;以及修改props中引用类型内部属性引发的隐性状态混乱问题&#xff0c;该问题是React入门的高频基础错误&#xff0c;核心原因是违背了React官方规定的props只读不…

作者头像 李华