Java 中的泛型(Generics)是 JDK 5 引入的核心语言特性,它允许在定义类、接口或方法时使用**类型参数(type parameters)**,从而将类型检查从**运行时**转移到**编译时**,提供更强的类型安全性和代码重用性。具体的类型在**编译时**通过类型参数指定,而不是延迟到运行时确定。
**一句话:泛型让“代码模板”与“数据类型”解耦,既保证类型安全,又消除强制类型转换,还能让编译器在编译期就发现类型错误。**
>[info]总结记忆口诀:
“编译期检查、运行时擦除;读写分离、上下界分清;
泛型类接口延迟型,泛型方法更灵活;
基本类型要装箱、数组创建要绕行。”
------------------------------------------------
一、为什么需要泛型(痛点回顾)
1. 原始集合(JDK 1.4 及以前)
```java
List list = new ArrayList();
list.add("hello");
list.add(123); // 编译通过,运行期才暴露问题
String s = (String) list.get(1); // ClassCastException
```
2. 问题总结
- 编译期无法发现类型不匹配。
- 取出时必须显式强转,代码臃肿且易错。
- 容易混入“异质”元素,破坏容器语义。
------------------------------------------------
二、泛型基本语法
1. 类型参数(Type Parameter)
用尖括号 `<>` 声明,放在类名 / 接口名 / 方法名之后。
惯例单个大写字母:
`T`(Type)、`E`(Element)、`K`(Key)、`V`(Value)、`S`、`U` 等。
2. 常见写法
- 泛型类:`class Box<T> { ... }`
- 泛型接口:`interface Comparable<T> { ... }`
- 泛型方法:`public <T> void foo(T t) { ... }`
------------------------------------------------
三、最常用示例
1. 泛型集合 —— 取代“原生”容器
```java
List<String> list = new ArrayList<>();
list.add("Java");
// list.add(123); // 编译错误,类型检查提前到编译期
String first = list.get(0); // 无需强转
```
2. 自定义泛型类 —— 类型安全的“盒子”
```java
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> age = new Pair<>("Tom", 30);
```
3. 泛型方法 —— 把类型参数放在**返回值前面**
```java
public class Util {
// 泛型方法:接收任意数组,交换下标 i,j 位置元素
public static <T> void swap(T[] a, int i, int j) {
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
String[] arr = {"A","B","C"};
Util.<String>swap(arr, 0, 2); // 可省略 <String>,编译器推断
```
4. 泛型接口 + 实现类
```java
public interface Calculator<T> {
T add(T a, T b);
}
public class IntCalc implements Calculator<Integer> {
public Integer add(Integer a, Integer b) {
return a + b;
}
}
```
------------------------------------------------
四、类型擦除(Type Erasure)—— 必须知道的底层机制
1. 编译期“去泛型化”:
- 所有 `<T>` 被替换成其**上限**(无显式上限则视为 `Object`)。
- 插入必要的强制转换代码。
- 生成“桥方法”以保证多态(泛型接口 / 继承场景)。
2. 后果
- 运行时无法获取 `T` 的实际类型 → `new T()`、`T.class`、`instanceof T` 均非法。
- 不同泛型参数的同类类共享同一份字节码:`List<String>.class == List<Integer>.class`。
------------------------------------------------
五、通配符(Wildcard)—— 让泛型“协变/逆变”
1. 无界通配符:`<?>`
表示“任意类型”,但**无法写入**(除 `null 外`)。
```java
void print(List<?> list) {
for (Object o : list) System.out.println(o);
}
```
2. 上界通配符:`<? extends T>`
可以安全**读**出为 `T`,但**不能写入**(除 `null`)。
```java
double sum(List<? extends Number> nums) {
double s = 0;
for (Number n : nums) s += n.doubleValue();
return s;
}
```
3. 下界通配符:`<? super T>`
可以安全**写入** `T` 及其子类,但**读出**只能是 `Object`。
```java
void fill(List<? super Integer> list) {
list.add(1);
list.add(2);
}
```
------------------------------------------------
六、类型参数的约束(Bounded Type Parameter)
让 `T` 必须实现某接口或继承某类:
```java
public <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
```
可多个界限:`T extends Number & Comparable<T>`(类在前,接口在后)。
------------------------------------------------
七、实战技巧与常见坑
1. 不能是基本类型:`List<int>` ❌ → `List<Integer>` ✅
2. 无法创建泛型数组:`new T[10]` ❌ → `(T[]) new Object[10]` 需强转并 `@SuppressWarnings`。
3. 静态上下文不能引用类级类型参数:
```java
class Foo<T> {
// static T field; // 编译错误
// static T method(T t){} // 编译错误
}
```
4. 重载冲突:擦除后方法签名相同导致编译失败。
5. 优先使用泛型方法而非“原生”`Object` 参数,可减少强制转换。
------------------------------------------------
八、完整综合示例 —— 实现一个类型安全的简易缓存
```java
public class Cache<K, V> {
private final Map<K, V> map = new HashMap<>();
public void put(K key, V value) { map.put(key, value); }
public Optional<V> get(K key) {
return Optional.ofNullable(map.get(key));
}
public <U extends V> void putAll(Map<K, U> entries) { // 泛型方法 + 上界
map.putAll(entries);
}
public static <K, V> Cache<K, V> create() { // 泛型静态工厂
return new Cache<>();
}
public static void main(String[] args) {
Cache<String, Integer> cache = Cache.create();
cache.put("age", 18);
cache.get("age").ifPresent(System.out::println); // 18
}
}
```
------------------------------------------------
九、总结记忆口诀
“编译期检查、运行时擦除;读写分离、上下界分清;
泛型类接口延迟型,泛型方法更灵活;
基本类型要装箱、数组创建要绕行。”
- 环境配置
- window怎么配置java环境变量?
- SQL学习
- 字段类型
- 1、 所有可声明的字段类型
- 2、常用的可声明的字段类型
- 创建表和表内字段
- 1、整个创建表语句
- 2、设置id主键
- 3、设置业务主键,唯一索引
- 4、设置业务字段
- 5、普通索引
- 6、唯一索引与普通索引的区别
- 7、表的引擎、字符集、排序规则和注释的设置
- Java基础语法
- 数据类型
- Java中的数据类型
- LocalDate
- 常用的数据类型
- Java 常用数据类型方法
- Java中数组、list、Map、HashMap
- 如何用Map来优化那些复杂的“双重 for 循环”查询?
- Java 8 中steam()操作流
- Java中HashMap和JSON
- Java中的JSONObject
- 工具类
- Java 常用工具类
- Arrays工具类
- Java 时间工具类
- 泛型
- Java中泛型概念
- Java中的泛型容器
- 泛型参数与函数参数的区别
- 推断出泛型实参
- Lambda表达式
- 数据分层
- 异常处理
- Java8 异常处理类总结表
- MyBatis-Plus 常用异常类总结表
- Java高级特性
- Maven
- jib-maven-plugin
- 什么是Spring Boot 的 parent pom?
- maven中各个生命周期的含义
- Spring Boot
- maven与spring boot 的关系
- Java中的连接池
- Spring JDBC
- Spring JDBC的概念
- JdbcTemplate常用的方法
- Spring中Bean的概念
- Spring中的抽象,通俗解释一下
- Spring中的事物
- Spring中的事物,通俗解释一下
- Spring中的事物抽象,常见的有哪些,列举一下
- Spring中常用的事物场景有哪些,列举一下
- Spring事务管理有哪些注解?
- Spring中使用事物处理订单的案例,列举说明一下
- Spring中声明式事务、分布式事务以及编程式事务的区别,列举一下
- 配置文件
- application-properties配置文件
- Spring Boot 的启动
- spring boot项目如何启动?
- 列举一下Spring Boot的启动过程
- SpringApplication.run方法
- Spring Boot 启动时有哪些接口?
- CommandLineRunner
- Spring Boot 的常用注解
- 系统注解
- 表格:系统注解
- @Override
- @Deprecated
- @SuppressWarnnings
- 使用在类名上的注解
- 表格:使用在类名上的注解
- @RestController
- @Controller
- @Service
- @Repository
- @Component
- @Configuration
- @Resource
- @Autowired
- @RequestMapping
- @GetMapping
- @PostMapping
- @Transactional
- @Qualifier
- 使用在方法上的注解
- 表格:使用在方法上的注解
- @RequestBody
- @PathVariable
- @Bean
- @ResponseBody
- @PreAuthorize
- 其他常用注解
- 表格:其他常用注解
- @EnableAutoConfiguration
- @SpringBootApplication
- @EnableScheduling
- @EnableAsync
- @ComponentScan
- @Aspec
- @ControllerAdvice
- @ExceptionHandler
- @Value
- @ConfigurationProperties
- @EnableConfigurationProperties
- @MapperScan
- @ApiOperation
- @Produces
- Validator验证的常用注解
- spring IoC容器
- Spring IoC容器依赖注入实现方式
- 常用依赖
- RESTEasy
- resteasy简介
- RESTEasy框架(依赖)的功能和常用注解
- MyBatis
- 简介
- paginationInterceptor
- @TableName
- @TableId
- @Param
- MyBatis-Plus
- MyBatis-Plus简介
- MyBatis-Plus的工具类
- Mybatis-Plus扩展的工具类和方法
- MyBatis-Plus中最常用的工具类方法
- MyBatis-Plus 中最常用的 4 大核心工具类
- Wrapper条件构造器
- Wrapper条件构造器详解
- Wrapper条件构造器eq等方法的参数说明
- LambdaQueryWrapper与QueryWrapper
- 日期格式是否必须转换
- Lombok
- Lombok作用详解
- @Data
- @Slf4j
- @Builder
- @EqualsAndHashCode
- @Accessors
- Jackson
- Jackson简介
- @JsonFormat
- Jackson高效地在 HashMap 和 JSON 字符串之间进行相互转换
- Hutool
- Hutool简介
- hutool依赖常用的方法
- fastjson2
- fastjson2简介
- UrlBasedCorsConfigurationSource
- 生态相关
- JBoss 社区
- 支付系统
- 1. 初始化mysql数据库流程
- 2. 初始化redis数据库的流程
- 3. 初始化rabbitmq服务
- 环球置业
- 1.模块目录结构分析
- 2. DTO(数据传输层)的核心作用
- 3. VO(视图对象层)
- 4. VO(视图对象层)和 DTO 数据传输层 的区别
