视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)
一、真实痛点:没有泛型的世界有多痛苦?
想象一下你在开发一个电商系统,需要写一个通用的订单缓存工具类:
// 没有泛型的写法(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> } }✅优势:
- 编译器知道
data是User或List<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 = Integer3. 泛型接口(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 | 零强转 |
| 复用性 | 每个类型写一套 | 一套代码通吃所有类型 |
✅记住:泛型不是“炫技”,而是写出健壮、可维护代码的基本功!
视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)