泛型定义
泛型三剑客:类、接口、方法
泛型不是一种语法糖,它是一种类型抽象机制。JDK 5 引入泛型后,Java 的类型系统多了一个维度——参数化类型。
简单说:泛型就是让类、接口、方法在定义时,接受一个「类型参数」,使用时才确定具体是什么类型。
泛型类
最常见的形式:
java
// 定义时用 <T> 占位
public class Box<T> {
private T content;
public void put(T item) {
this.content = item;
}
public T get() {
return content;
}
}
// 使用时指定具体类型
Box<String> stringBox = new Box<>();
stringBox.put("苹果");
String fruit = stringBox.get(); // 不需要转型多类型参数
一个类可以有多个类型参数:
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> entry = new Pair<>("年龄", 25);类型参数命名约定
不是关键字,但有行业约定:
java
T - Type(类型)
E - Element(集合元素)
K - Key(键)
V - Value(值)
R - Return(返回值)
S/U/V - 第二/三/四个类型泛型接口
接口同样可以泛型化:
java
// 定义泛型接口
public interface Generator<T> {
T produce();
}
// 实现时指定类型
public class StringGenerator implements Generator<String> {
@Override
public String produce() {
return "Hello";
}
}
// 或者继续保留泛型
public class GenericGenerator<T> implements Generator<T> {
private T product;
public GenericGenerator(T product) {
this.product = product;
}
@Override
public T produce() {
return product;
}
}泛型方法
方法级别的泛型,独立于类的泛型声明:
java
public class Arrays {
// 交换任意类型数组的两个元素
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 返回第一个元素
public static <T> T first(T[] array) {
if (array == null || array.length == 0) {
return null;
}
return array[0];
}
}
// 调用
String[] names = {"Alice", "Bob", "Carol"};
Arrays.swap(names, 0, 2); // 自动推断 T = String
String first = Arrays.first(names);方法泛型的位置
注意 <T> 写在返回值前面,这是固定的语法位置:
java
// 正确
public static <T> T method(T arg) { ... }
// 错误
public static T <T> method(T arg) { ... } // 这样写 T 会被当成注解类型约束
用 extends 限制泛型能接受的范围:
java
// T 必须是 Number 或其子类
public class NumberBox<T extends Number> {
private T value;
public double toDouble() {
return value.doubleValue(); // 因为限定了范围,可以安全调用 Number 的方法
}
}
NumberBox<Integer> ok = new NumberBox<>(); // OK
NumberBox<String> fail = new NumberBox<>(); // 编译错误多约束(用 & 连接):
java
// T 必须是 Comparable 且 Serializable
public class <T extends Comparable & Serializable> void process(T item) { ... }Diamond 语法
JDK 7 引入的简化写法,编译器自动推断类型:
java
// 完整写法
Box<String> box = new Box<String>();
// Diamond 语法(JDK 7+)
Box<String> box = new Box<>();类型推断
编译器能自动推断泛型参数:
java
// 编译器推断 T = String
static <T> T identity(T arg) {
return arg;
}
String s = identity("hello"); // 不需要显式指定类型
Integer i = identity(123); // 推断 T = Integer常见错误
不能 new T()
java
public <T> T create() {
return new T(); // 编译错误
}因为类型擦除后,T 在运行时不存在。
解决方案:使用反射或工厂模式。
不能创建泛型数组
java
public <T> T[] createArray() {
return new T[10]; // 编译错误
}解决方案:Array.newInstance() 或传入 Class 对象。
不能用基本类型
java
List<int> list; // 编译错误
List<Integer> list; // 正确泛型要求类型都是对象,基本类型需要包装成包装类。
总结
泛型定义有三种形式:
| 形式 | 关键字 | 示例 |
|---|---|---|
| 泛型类 | <T> | class Box<T> |
| 泛型接口 | <T> | interface Generator<T> |
| 泛型方法 | <T> | static <T> void swap(T[] a, int i, int j) |
记住:泛型是一种编译时机制,运行时泛型信息会被擦除。
