名称: java-generics 用户可调用: 假 描述: 当需要Java泛型包括类型参数、通配符和类型边界时使用。用于编写类型安全可复用的代码。 允许的工具:
- Bash
- Read
- Write
- Edit
Java泛型
掌握Java的泛型系统,用于编写类型安全、可复用的代码,支持编译时类型检查、泛型类、方法、通配符和类型边界。
泛型介绍
泛型允许在定义类、接口和方法时将类型作为参数,提供编译时类型安全。
基本泛型类:
public class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
public static void main(String[] args) {
// 字符串的类型安全盒子
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // 无需类型转换
// 整数的类型安全盒子
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer number = intBox.get();
}
}
带多个类型参数的泛型:
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; }
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("age", 30);
String key = pair.getKey();
Integer value = pair.getValue();
}
}
泛型方法
泛型方法可以独立于泛型类定义。
基本泛型方法:
public class GenericMethods {
// 泛型方法
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// 带返回类型的泛型方法
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}
public static void main(String[] args) {
String[] strings = {"a", "b", "c"};
Integer[] numbers = {1, 2, 3};
printArray(strings); // T推断为String
printArray(numbers); // T推断为Integer
String first = getFirst(strings);
Integer firstNum = getFirst(numbers);
}
}
带多个类型参数的泛型方法:
public class MultiplTypeParams {
public static <K, V> Map<K, V> createMap(K key, V value) {
Map<K, V> map = new HashMap<>();
map.put(key, value);
return map;
}
public static <T, R> R transform(T input, Function<T, R> transformer) {
return transformer.apply(input);
}
public static void main(String[] args) {
Map<String, Integer> map = createMap("count", 10);
String result = transform(42, num -> "Number: " + num);
// 结果: "Number: 42"
}
}
有界类型参数
类型边界限制可用作类型参数的类型。
上界类型参数:
public class UpperBound {
// T必须是Number或其子类
public static <T extends Number> double sum(List<T> numbers) {
double total = 0;
for (T num : numbers) {
total += num.doubleValue();
}
return total;
}
// 多个边界
public static <T extends Comparable<T> & Serializable> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
public static void main(String[] args) {
List<Integer> integers = List.of(1, 2, 3, 4, 5);
double sum = sum(integers); // 15.0
List<Double> doubles = List.of(1.5, 2.5, 3.5);
double doubleSum = sum(doubles); // 7.5
String maxStr = max("apple", "banana"); // "banana"
}
}
带边界类型参数的类:
public class NumberBox<T extends Number> {
private T number;
public NumberBox(T number) {
this.number = number;
}
public double doubleValue() {
return number.doubleValue();
}
public boolean isZero() {
return number.doubleValue() == 0.0;
}
public static void main(String[] args) {
NumberBox<Integer> intBox = new NumberBox<>(42);
NumberBox<Double> doubleBox = new NumberBox<>(3.14);
// 编译错误: String不是Number
// NumberBox<String> stringBox = new NumberBox<>("fail");
}
}
通配符
通配符在处理泛型类型时提供灵活性。
无界通配符:
public class UnboundedWildcard {
// 接受任何List
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
public static int size(List<?> list) {
return list.size();
}
public static void main(String[] args) {
List<String> strings = List.of("a", "b", "c");
List<Integer> integers = List.of(1, 2, 3);
printList(strings);
printList(integers);
System.out.println(size(strings)); // 3
System.out.println(size(integers)); // 3
}
}
上界通配符:
public class UpperBoundedWildcard {
// 接受Number或其任何子类的List
public static double sum(List<? extends Number> numbers) {
double total = 0;
for (Number num : numbers) {
total += num.doubleValue();
}
return total;
}
public static void main(String[] args) {
List<Integer> integers = List.of(1, 2, 3);
List<Double> doubles = List.of(1.5, 2.5);
List<Number> numbers = List.of(1, 2.5, 3);
System.out.println(sum(integers)); // 6.0
System.out.println(sum(doubles)); // 4.0
System.out.println(sum(numbers)); // 6.5
}
}
下界通配符:
public class LowerBoundedWildcard {
// 接受Integer或其任何超类的List
public static void addIntegers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i);
}
}
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>();
addIntegers(integers);
System.out.println(integers); // [1, 2, 3, 4, 5]
List<Number> numbers = new ArrayList<>();
addIntegers(numbers);
System.out.println(numbers); // [1, 2, 3, 4, 5]
List<Object> objects = new ArrayList<>();
addIntegers(objects);
System.out.println(objects); // [1, 2, 3, 4, 5]
}
}
PECS原则
生产者扩展,消费者超级 - 使用通配符的指导原则。
PECS在行动中:
public class PECSExample {
// 生产者 - 从源读取(扩展)
public static <T> void copy(
List<? extends T> source,
List<? super T> destination
) {
for (T item : source) {
destination.add(item);
}
}
// 生产者 - 扩展用于读取
public static double sumNumbers(List<? extends Number> numbers) {
double sum = 0;
for (Number num : numbers) { // 读取(生成值)
sum += num.doubleValue();
}
return sum;
}
// 消费者 - 超级用于写入
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 3; i++) {
list.add(i); // 写入(消耗值)
}
}
public static void main(String[] args) {
List<Integer> source = List.of(1, 2, 3);
List<Number> destination = new ArrayList<>();
copy(source, destination);
System.out.println(destination); // [1, 2, 3]
}
}
泛型接口
接口可以是泛型的,为泛型类型提供契约。
泛型接口:
public interface Repository<T, ID> {
T findById(ID id);
List<T> findAll();
void save(T entity);
void delete(ID id);
}
public class UserRepository implements Repository<User, Long> {
private Map<Long, User> storage = new HashMap<>();
@Override
public User findById(Long id) {
return storage.get(id);
}
@Override
public List<User> findAll() {
return new ArrayList<>(storage.values());
}
@Override
public void save(User user) {
storage.put(user.getId(), user);
}
@Override
public void delete(Long id) {
storage.remove(id);
}
}
class User {
private Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() { return id; }
public String getName() { return name; }
}
Comparable和Comparator:
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
return this.name.compareTo(other.name);
}
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
// 自然排序(按姓名)
Collections.sort(people);
// 自定义比较器(按年龄)
Comparator<Person> ageComparator =
Comparator.comparingInt(p -> p.age);
people.sort(ageComparator);
}
}
类型擦除
Java泛型使用类型擦除 - 泛型类型信息在运行时被移除。
理解类型擦除:
public class TypeErasure {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// 在运行时,两者都只是List
System.out.println(strings.getClass() == integers.getClass());
// true
// 不能在运行时检查泛型类型
// if (list instanceof List<String>) {} // 编译错误
// 只能检查原始类型
if (strings instanceof List) {
System.out.println("Is a List");
}
}
}
类型擦除的后果:
public class ErasureConsequences<T> {
// 不能创建类型参数的实例
// T instance = new T(); // 编译错误
// 不能创建参数化类型的数组
// T[] array = new T[10]; // 编译错误
// 不能使用instanceof与类型参数
public boolean isInstance(Object obj) {
// if (obj instanceof T) {} // 编译错误
return true;
}
// 解决方法: 传递Class<T>
private Class<T> type;
public ErasureConsequences(Class<T> type) {
this.type = type;
}
public T createInstance() throws Exception {
return type.getDeclaredConstructor().newInstance();
}
@SuppressWarnings("unchecked")
public T[] createArray(int size) {
return (T[]) Array.newInstance(type, size);
}
}
泛型构建器
使用泛型的构建器模式,用于流畅的API。
泛型构建器:
public class Query<T> {
private final Class<T> type;
private String where;
private String orderBy;
private int limit;
private Query(Class<T> type) {
this.type = type;
}
public static <T> Query<T> from(Class<T> type) {
return new Query<>(type);
}
public Query<T> where(String condition) {
this.where = condition;
return this;
}
public Query<T> orderBy(String field) {
this.orderBy = field;
return this;
}
public Query<T> limit(int count) {
this.limit = count;
return this;
}
public List<T> execute() {
// 执行查询并返回结果
return new ArrayList<>();
}
public static void main(String[] args) {
List<User> users = Query.from(User.class)
.where("age > 18")
.orderBy("name")
.limit(10)
.execute();
}
}
递归类型边界
类型边界可以引用类型参数本身。
带递归边界的枚举:
public class RecursiveBound {
// 枚举技巧
public static <E extends Enum<E>> void printEnum(Class<E> enumClass) {
for (E constant : enumClass.getEnumConstants()) {
System.out.println(constant);
}
}
// 带递归边界的Comparable
public static <T extends Comparable<T>> T max(List<T> list) {
if (list.isEmpty()) {
throw new IllegalArgumentException("Empty list");
}
T max = list.get(0);
for (T item : list) {
if (item.compareTo(max) > 0) {
max = item;
}
}
return max;
}
enum Color { RED, GREEN, BLUE }
public static void main(String[] args) {
printEnum(Color.class);
List<String> words = List.of("apple", "banana", "cherry");
String maxWord = max(words); // "cherry"
List<Integer> numbers = List.of(1, 5, 3, 9, 2);
Integer maxNum = max(numbers); // 9
}
}
带递归边界的构建器:
public abstract class Builder<T, B extends Builder<T, B>> {
protected abstract B self();
public abstract T build();
}
public class Person {
private final String name;
private final int age;
protected Person(PersonBuilder<?> builder) {
this.name = builder.name;
this.age = builder.age;
}
public static PersonBuilder<?> builder() {
return new PersonBuilder<>();
}
public static class PersonBuilder<B extends PersonBuilder<B>>
extends Builder<Person, B> {
private String name;
private int age;
public B name(String name) {
this.name = name;
return self();
}
public B age(int age) {
this.age = age;
return self();
}
@Override
@SuppressWarnings("unchecked")
protected B self() {
return (B) this;
}
@Override
public Person build() {
return new Person(this);
}
}
}
何时使用此技能
在以下情况下使用java-generics:
- 编写可处理多种类型的可复用代码
- 强制编译时类型安全
- 消除运行时类型转换和错误
- 创建泛型集合、算法或工具
- 构建类型安全的API和框架
- 实现通用设计模式
- 使用Java集合框架
- 定义带类型参数的灵活方法签名
- 创建有界类型层次结构
- 以类型安全方式实现构建器或工厂模式
最佳实践
- 使用有意义的类型参数名称(如T, E, K, V)
- 优先使用有界类型参数而非原始类型
- 在方法参数中使用通配符以增加灵活性
- 应用PECS原则(生产者扩展,消费者超级)
- 在新代码中避免使用原始类型
- 谨慎使用@SuppressWarnings(“unchecked”)
- 清晰地记录泛型类型约束
- 尽可能使用泛型方法而非泛型类
- 使用有界通配符以实现最大API灵活性
- 考虑类型擦除的影响
常见陷阱
- 使用原始类型而非参数化类型
- 混淆扩展和超级通配符
- 尝试创建泛型类型的数组
- 不理解类型擦除的限制
- 过度使用通配符导致代码难以阅读
- 通配符的方差使用错误
- 忘记泛型仅在编译时有效
- 未能正确处理未检查警告
- 创建不必要复杂的泛型层次结构
- 错误地在泛型类型中使用instanceof