别再被泛型吓到了!T、E、K、V、? 到底是啥?一篇讲透!

6,296 阅读3分钟

你有没有过这种经历?

看别人写代码,动不动就是 List<T>Map<K, V>,甚至还有个神秘的 ?
心里直犯嘀咕:这些字母到底是干啥的?能不能换成 A、B、C?
想问吧,又怕显得自己太菜……别慌,今天咱们就彻底搞明白它们!


一、泛型是啥?一句话说清

泛型 = “类型占位符”

想象一下:你想写一个“盒子”类,既能装字符串,也能装数字,还能装自定义对象。
难道要为每种类型都写一个类?那不得累死!

于是 Java 引入了泛型——先用一个“代号”占着位置,等真正用的时候再告诉它:“这次装的是 String”。

这样,一份代码,通吃所有类型,安全又高效!


二、那些字母 T、E、K、V……是规定吗?

不是语法强制,而是社区约定

你完全可以写成 List<A>Box<B>,编译器不会报错。
但别人一看:这人是不是刚学?怎么不按套路出牌?

所以,大家默认用这几个字母,不是因为技术限制,而是为了“看得懂”

下面逐个拆解:


T → Type(类型)

最通用的泛型符号,代表“任意类型”。

public class Holder<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

用的时候:

Holder<String> h1 = new Holder<>();
Holder<Integer> h2 = new Holder<>();

这里的 T 就像一个“变量名”,只是这个变量代表的是类型,不是值。


E → Element(元素)

专门用于集合类,比如 List<E>Set<E>

JDK 源码里就这么写的:

public interface List<E> {
    boolean add(E e);
    get(int index);
}

为什么不用 T?因为 E 更有语义——一看就知道这是“集合里的元素”。

比如:

List<String> names = new ArrayList<>(); // E 就是 String

所以:T 是通用型,E 是集合专用型,本质没区别,但写法更专业。


K 和 V → Key 和 Value

这对搭档只出现在 Map 里,毫无悬念:

public interface Map<K, V> {
    V put(K key, V value);
    V get(Object key);
}
  • K = 键的类型(Key)
  • V = 值的类型(Value)

例子:

Map<String, Integer> ageMap = new HashMap<>();
ageMap.put("张三"25); // K=String, V=Integer

记住:看到 Map,就想到 K 和 V,别乱换字母,不然队友会懵。


? → 通配符(Wildcard):表示“我不在乎类型”

这个最让人头大,其实很简单:当你不需要知道具体类型时,就用 ?

比如,你只想打印一个列表的长度,管它里面是啥呢?

public void printSize(List<?> list) {
    System.out.println("列表大小:" + list.size());
}

这里 ? 表示:任何类型的 List 都可以传进来

✅ 可以调用 size()isEmpty() 这种不依赖元素类型的方法。
❌ 不能往里面 add() 元素(因为不知道该加什么类型)。
✅ 可以读取元素,但只能当成 Object 用。

进阶用法(了解即可):

  • <? extends Animal>:可以是 Animal 或它的子类(上限通配符
  • <? super Dog>:可以是 Dog 或它的父类(下限通配符

日常开发中,大多数时候用 ? 就够了。


四、总结:一张表搞定所有符号

符号含义常见场景能否替换?
TType(类型)通用类、方法能,但别乱换
EElement(元素)List<E>Set<E>能,但建议保留语义
KKeyMap<K, V>强烈建议别换
VValueMap<K, V>同上
?通配符不关心具体类型时不能替换成字母

泛型不是魔法,它只是让代码更安全、更复用的一种设计。
那些字母也不是密码,只是程序员之间的“行话”。

用多了,自然就熟了。下次看到 Map<K, V>,你可以自信地说:
“哦,K 是键,V 是值,小意思!”

搞定,收工!🚀