Java 泛型通配符怎么用
你有没有过这种经历:想把不同类型的水果放进同一个篮子里,却被提醒“苹果篮子不能装橘子”?在编程里,这种“装不同类型内容”的需求很常见,而Java里的泛型通配符,就是帮我们解决这个问题的小工具。
先搞懂泛型的基础:为什么需要通配符
先回忆下普通泛型:如果我们定义一个“装苹果的篮子”是List<Apple>,那这个篮子就只能放苹果,连“苹果的爸——水果”都放不进去,更别说橘子了。但如果我们要写一个函数,比如“给所有水果篮子加标签”,就会卡住:总不能写10个函数,分别给苹果篮、香蕉篮、橘子篮加标签吧?这时候通配符就派上用场了——它能让我们“模糊处理”类型,不用盯死某一种具体类型。

通配符的核心符号:?是什么意思
通配符最常用的是?,它就像“万能占位符”,代表“任意一种未知类型”。举个例子:如果我们写List<?> basket,就等于告诉Java:“这个篮子不管装苹果、橘子还是别的什么,我都认”。这样,刚才的“加标签”函数就可以写成:
`java
void addTag(List<?> basket, String tag) {
// 给这个篮子加标签,不管里面装什么
}
`
这样不管传List<Apple>还是List<Orange>,都能正常调用,不用写重复代码了。
两种约束:上限和下限,别乱装东西
光有“任意类型”还不够,有时候我们要限制范围,不然可能会出乱子。通配符有两种常用的约束,就像给篮子加了“只能装XX范围的东西”的规矩。
上限约束:只能装“某类及它的子类”
用? extends 类名表示,相当于“这个篮子只能装XX家族的人,包括XX自己”。比如List<? extends Fruit>,就是说“只能装Fruit(水果)和它的子类,比如Apple、Orange”。
举个生活化例子:你开了个“水果家族专属篮”,这个篮子不能装狗、猫这些非水果,只能装苹果、香蕉、橘子——都是水果的后代。对应的代码里,你可以拿里面的东西当Fruit用,因为不管是Apple还是Orange,都是Fruit,吃起来都是水果的味道。
下限约束:只能装“某类及它的父类”
用? super 类名表示,相当于“这个篮子只能装XX本人和他的长辈,不能装晚辈”。比如List<? super Apple>,就是说“这个篮子只能装Apple和它的父类Fruit、Object,不能装Orange(Orange是Apple的同辈,不是父类)”。
还是用篮子举例:你有个“苹果家的长辈篮”,只能放Apple的爸爸(Fruit)、爷爷(Object),不能放Apple的弟弟(Orange)——放进去会乱辈分。这种约束一般用在“往篮子里加东西”的时候,保证加的是允许的类型。
举个实际场景:用通配符写通用函数
假设我们要写一个函数,统计水果篮子里的水果数量,不管装的是苹果、橘子还是混合的水果。用通配符就能轻松搞定:
`java
// 统计任何水果家族篮子的数量
int countFruits(List<? extends Fruit> fruitBasket) {
return fruitBasket.size();
}
`
这个函数既能统计List<Apple>的数量,也能统计List<Orange>的数量,还能统计List<Fruit>的数量,完美解决了重复函数的问题。
避坑提醒:通配符不是万能的
通配符虽然好用,但也有小陷阱。比如List<?>的篮子,你往里面加东西的时候,会被Java拒绝——因为你不知道里面装的是什么类型,万一你加个橘子进去,结果它本来是苹果篮,就会出错。所以如果要往篮子里加内容,用下限约束(? super 类名)更合适;如果只是拿东西出来,用上限约束(? extends 类名)更安全。
其实泛型通配符就像生活里的“通用标签”,不用给每个小篮子都单独做标签,既能灵活处理不同类型,又不会乱套。下次写代码遇到“不同类型的同功能处理”,记得用通配符这个小工具哦。