问题

泛型中K、T、V、E、Object等分别代表什么含义?

答案

核心概念

泛型中的字母是类型参数(Type Parameter) 的占位符,本质上可以使用任意标识符,但Java约定使用单个大写字母命名,以提高代码可读性。

常用类型参数约定

字母 含义 英文全称 典型使用场景
T 类型 Type 通用类型参数
E 元素 Element 集合元素类型
K Key Map的键类型
V Value Map的值类型
N 数字 Number 数值类型
S, U, V 第2、3、4个类型 2nd, 3rd, 4th type 多类型参数
? 通配符 Wildcard 不确定的类型

1. T - Type(类型)

最常用的泛型参数,表示任意类型。

// 泛型类
public class Box<T> {
    private T data;

    public void set(T data) {
        this.data = data;
    }

    public T get() {
        return data;
    }
}

// 泛型方法
public <T> T getFirst(List<T> list) {
    return list.isEmpty() ? null : list.get(0);
}

// 泛型接口
public interface Comparable<T> {
    int compareTo(T other);
}

2. E - Element(元素)

集合框架中广泛使用,表示集合中的元素类型。

// List接口
public interface List<E> extends Collection<E> {
    boolean add(E e);
    E get(int index);
    E remove(int index);
}

// Set接口
public interface Set<E> extends Collection<E> {
    boolean add(E e);
    boolean contains(Object o);
}

// 使用
List<String> names = new ArrayList<>();
names.add("Alice");
String first = names.get(0);

Set<Integer> numbers = new HashSet<>();
numbers.add(100);

3. K, V - Key, Value(键值对)

Map集合中使用,K表示键,V表示值。

// Map接口
public interface Map<K, V> {
    V put(K key, V value);
    V get(Object key);
    Set<K> keySet();
    Collection<V> values();
    Set<Map.Entry<K, V>> entrySet();
}

// Map.Entry内部接口
interface Entry<K, V> {
    K getKey();
    V getValue();
    V setValue(V value);
}

// 使用
Map<String, User> userMap = new HashMap<>();
userMap.put("user001", new User("张三"));
User user = userMap.get("user001");

// 遍历Map
for (Map.Entry<String, User> entry : userMap.entrySet()) {
    String key = entry.getKey();
    User value = entry.getValue();
}

4. N - Number(数字)

表示数值类型,通常与数学计算相关。

// 数值计算工具类
public class MathUtils {
    // 求和
    public static <N extends Number> double sum(List<N> numbers) {
        return numbers.stream()
            .mapToDouble(Number::doubleValue)
            .sum();
    }

    // 求最大值
    public static <N extends Number & Comparable<N>> N max(N a, N b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

// 使用
List<Integer> ints = Arrays.asList(1, 2, 3);
double sum = MathUtils.sum(ints);

Integer max = MathUtils.max(10, 20);

5. S, U, V - 多类型参数

当需要多个类型参数时,使用S、U、V等字母。

// 函数式接口
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

// 三元组
public class Triple<T, U, V> {
    private final T first;
    private final U second;
    private final V third;

    public Triple(T first, U second, V third) {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    public T getFirst() { return first; }
    public U getSecond() { return second; }
    public V getThird() { return third; }
}

// 使用
Triple<String, Integer, Boolean> triple =
    new Triple<>("名称", 100, true);

6. ? - Wildcard(通配符)

表示不确定的类型,只能用于声明,不能用于定义。

// 无界通配符
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 上界通配符
public double sumOfNumbers(List<? extends Number> numbers) {
    return numbers.stream()
        .mapToDouble(Number::doubleValue)
        .sum();
}

// 下界通配符
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

// 使用
List<Integer> ints = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.5, 2.5);

printList(ints);        // ✅ 可以传递任意类型的List
sumOfNumbers(ints);     // ✅ 可以传递Number及其子类的List
sumOfNumbers(doubles);  // ✅

类型参数 vs Object

虽然类型擦除后都变成Object,但泛型提供编译期类型检查。

// 使用Object
public class ObjectBox {
    private Object data;

    public void set(Object data) {
        this.data = data;
    }

    public Object get() {
        return data;
    }
}

// 使用泛型
public class GenericBox<T> {
    private T data;

    public void set(T data) {
        this.data = data;
    }

    public T get() {
        return data;
    }
}

// 对比
ObjectBox objBox = new ObjectBox();
objBox.set("字符串");
objBox.set(100); // ✅ 编译通过,但类型不安全
String str = (String) objBox.get(); // ❌ 运行时ClassCastException

GenericBox<String> genBox = new GenericBox<>();
genBox.set("字符串");
// genBox.set(100); // ❌ 编译错误,类型安全
String str = genBox.get(); // ✅ 无需强制转换

自定义命名规范

虽然可以使用任意标识符,但建议遵循约定。

// ❌ 不推荐:使用完整单词
public class Container<ElementType> {
    private ElementType data;
}

// ✅ 推荐:使用单个大写字母
public class Container<E> {
    private E data;
}

// ✅ 语义明确的场景可以使用缩写
public class Repository<Entity, ID> {
    public Entity findById(ID id) { ... }
}

// ✅ 或使用约定字母
public class Repository<T, ID> {
    public T findById(ID id) { ... }
}

实战应用场景

1. 通用响应封装(T)

public class Result<T> {
    private int code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.code = 200;
        result.data = data;
        return result;
    }

    public static <T> Result<T> error(String message) {
        Result<T> result = new Result<>();
        result.code = 500;
        result.message = message;
        return result;
    }
}

// 使用
Result<User> userResult = Result.success(user);
Result<List<Order>> orderResult = Result.success(orders);

2. 分页结果(E)

public class Page<E> {
    private int pageNum;
    private int pageSize;
    private long total;
    private List<E> records;

    // Getter/Setter
}

// 使用
Page<User> userPage = userService.findUsers(1, 10);
Page<Product> productPage = productService.findProducts(2, 20);

3. 键值对缓存(K, V)

public class Cache<K, V> {
    private Map<K, V> cache = new ConcurrentHashMap<>();

    public void put(K key, V value) {
        cache.put(key, value);
    }

    public V get(K key) {
        return cache.get(key);
    }

    public void remove(K key) {
        cache.remove(key);
    }
}

// 使用
Cache<String, User> userCache = new Cache<>();
userCache.put("user001", user);

Cache<Long, Order> orderCache = new Cache<>();
orderCache.put(1001L, order);

4. 数据转换器(S, T)

@FunctionalInterface
public interface Converter<S, T> {
    T convert(S source);
}

// 实现
Converter<String, Integer> stringToInt = Integer::parseInt;
Converter<User, UserDTO> userToDTO = user ->
    new UserDTO(user.getName(), user.getAge());

// 使用
Integer num = stringToInt.convert("123");
UserDTO dto = userToDTO.convert(user);

5. Builder模式(T)

public class Builder<T> {
    private final T instance;

    public Builder(Class<T> clazz) throws Exception {
        this.instance = clazz.newInstance();
    }

    public Builder<T> set(String property, Object value) {
        // 使用反射设置属性
        return this;
    }

    public T build() {
        return instance;
    }
}

// 使用
User user = new Builder<>(User.class)
    .set("name", "张三")
    .set("age", 25)
    .build();

JDK源码中的应用

1. Collections工具类

public class Collections {
    // 单一类型参数
    public static <T> void sort(List<T> list) { ... }

    // 多类型参数
    public static <T, S extends T> void copy(List<? super T> dest,
                                              List<? extends S> src) { ... }

    // 通配符
    public static void reverse(List<?> list) { ... }
}

2. Optional类

public final class Optional<T> {
    private final T value;

    public static <T> Optional<T> of(T value) { ... }
    public static <T> Optional<T> ofNullable(T value) { ... }
    public T get() { ... }

    public <U> Optional<U> map(Function<? super T, ? extends U> mapper) { ... }
}

3. Stream API

public interface Stream<T> {
    Stream<T> filter(Predicate<? super T> predicate);

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

    <R> R collect(Collector<? super T, A, R> collector);
}

命名最佳实践

// ✅ 好的命名
public class Pair<K, V> { }           // 键值对
public interface List<E> { }          // 列表元素
public class Optional<T> { }          // 通用类型
public interface Comparable<T> { }    // 通用类型

// ⚠️ 可接受的命名(语义明确)
public class Repository<Entity, ID> { }
public class Converter<Source, Target> { }

// ❌ 避免的命名(过于冗长)
public class Container<ElementType> { }
public class DataStructure<DataType> { }

答题总结

泛型类型参数是占位符,Java约定使用单个大写字母命名:

  • T(Type):通用类型,最常用
  • E(Element):集合元素类型(List、Set)
  • K(Key)、V(Value):Map的键值类型
  • N(Number):数值类型
  • S、U、V:多类型参数场景
  • ?(Wildcard):通配符,表示不确定的类型

这些约定提高了代码可读性,虽然可以使用任意标识符(如ElementType),但推荐遵循单字母约定。泛型相比Object的优势在于编译期类型检查,避免运行时类型转换错误。