你有没有过这种经历?
看别人写代码,动不动就是 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);
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或它的父类(下限通配符)
日常开发中,大多数时候用 ? 就够了。
四、总结:一张表搞定所有符号
| 符号 | 含义 | 常见场景 | 能否替换? |
|---|---|---|---|
T | Type(类型) | 通用类、方法 | 能,但别乱换 |
E | Element(元素) | List<E>, Set<E> | 能,但建议保留语义 |
K | Key | Map<K, V> | 强烈建议别换 |
V | Value | Map<K, V> | 同上 |
? | 通配符 | 不关心具体类型时 | 不能替换成字母 |
泛型不是魔法,它只是让代码更安全、更复用的一种设计。
那些字母也不是密码,只是程序员之间的“行话”。
用多了,自然就熟了。下次看到 Map<K, V>,你可以自信地说:
“哦,K 是键,V 是值,小意思!”
搞定,收工!🚀