首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >函数对象与泛型:Java编程核心解析

函数对象与泛型:Java编程核心解析

作者头像
北极的代码
发布2026-04-22 18:20:35
发布2026-04-22 18:20:35
540
举报
1. 函数对象 定义与由来 函数对象(Functor,在C++等语言中的称呼;在Java中通常通过Comparator或函数式接口实现)是指行为类似于函数的对象
  • 由来:在C语言时代,我们通过函数指针来将行为(如比较、排序规则)传递给数据结构(如qsort函数)。但函数指针有几个缺点:
    1. 无法保存状态(即函数内不能有记忆上一次调用的信息)。
    2. 内联困难,性能可能受影响。
    3. 语法较复杂。
  • 概念:为了解决这个问题,面向对象语言允许我们创建一个类,该类重载了括号运算符 operator() (C++),或者实现了某个特定接口(Java中的ComparatorRunnable)。这个类的实例就是一个对象,但可以像函数一样被调用。
  • 在数据结构中的应用 在数据结构与算法中,函数对象主要用于定制操作策略
  • 排序:当你定义一个二叉搜索树或使用排序函数时,你想让树按照升序排列还是降序排列?这个“比较规则”就是一个函数对象。
  • 遍历:当你遍历一个集合(如树、图)时,你想对每个元素做什么(打印、修改、求和)?这个“操作”就是一个函数对象。
  • 在Java 8之后,Lambda表达式简化了函数对象的书写,但背后的原理依然是函数对象(函数式接口

2. 泛型 由来:解决类型安全问题 在泛型出现之前(Java 1.4及以前,C通过void*),我们使用Object类型来构建通用的数据结构(如ArrayList可以存任何东西)。

  • 问题
    1. 需要强制类型转换:取出Object时,必须向下转型为具体类型,代码臃肿。
    2. 运行时错误:无法在编译时检查类型。例如,可以创建一个存有“字符串”的列表,却试图把它当作“整数”列表取出,导致ClassCastException
  • 概念:泛型(Generics)实现了参数化类型。它允许你在定义类、接口或方法时,将类型作为一个参数(占位符)使用。例如 ArrayList<String> 中的 String 就是类型参数。

在数据结构中的应用 几乎所有的现代数据结构都是泛型的。

  • 复用性:你不需要为 IntListStringListStudentList 分别写代码。写一个 List<T> 就够了。
  • 编译时检查:如果你声明了一个 List<String>,向里面添加Integer对象会在编译时报错。

3. 包装类 由来:弥补“一切皆对象”的缺口 在纯粹的面向对象语言(如Java)中,泛型只能使用引用类型(对象),不能使用基本类型(如 intdoublechar)。然而,intdouble 在数学计算和性能上又必不可少。

  • 概念:为了解决这个矛盾,包装类(Wrapper Classes)应运而生。它们将基本类型“包装”成对象。
    • int -> Integer
    • double -> Double
    • char -> Character

自动装箱/拆箱 为了使用方便,现代语言(Java 5+)支持自动装箱(Autoboxing)和自动拆箱(Unboxing)。

  • 装箱int i = 5; Integer obj = i; (自动将基本类型转为对象)
  • 拆箱Integer obj = 5; int i = obj; (自动将对象转为基本类型)

在数据结构中的应用 由于泛型无法直接接受基本类型,包装类成为了桥梁。

  1. 泛型 提供了数据结构(如二叉查找树、哈希表)的骨架,使其能够容纳任意类型的数据。
  2. 基本类型 不能直接放入泛型容器,所以 包装类 作为中间层,让基本类型的数据能够进入这个骨架。
  3. 函数对象 提供了数据结构中算法的灵魂。例如,在泛型的 PriorityQueue<T> 中,通过传入一个 Comparator<T> 函数对象,你可以动态地改变“优先级”的定义(是最小值优先还是最大值优先)。

通配符

通配符使用问号 ? 来表示未知类型。它主要出现在变量声明方法参数返回值中,用来解决泛型类型不一致的问题。

1. 为什么要用通配符?

场景:你想写一个方法,打印任意类型的列表。

java

代码语言:javascript
复制
// 假设没有通配符,你可能会这么写:
public static void printList(List<Object> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

问题:这个方法只能接受 List<Object>,而无法接受 List<String>List<Integer>。 因为 List<String> 不是 List<Object> 的子类,即使 StringObject 的子类。这就是所谓的泛型不变性

解决方案:使用通配符 List<?>

代码语言:javascript
复制
public static void printList(List<?> list) { // 可以接收任何类型的 List
    for (Object obj : list) { // 因为类型未知,所以只能当作 Object 处理
        System.out.println(obj);
    }
}
2. 形象解释:仓库管理员

想象你是一个仓库管理员,面前有几个盒子:

  • List<Apple>:一个只装苹果的盒子。
  • List<Fruit>:一个可以装各种水果的盒子。
  • List<Object>:一个什么都能装的万能盒子。

通配符是什么?

  • 无界通配符 List<?>: 这就像让你去接收一个密封的盒子。你知道里面肯定是水果(对象),但不知道具体是什么。
    • 你能做什么?你只能数数盒子有多大(list.size()),或者把盒子里的东西当作普通物品拿出来看看(赋值给 Object),因为你不知道具体是什么水果,所以你不能往里面放东西(除了 null)。
3. 上界通配符 <? extends T>

定义:表示类型是 TT 的子类。

形象解释:只读的展示柜

你有一个 List<? extends Fruit>,这相当于一个展示水果的玻璃柜

  • 里面有什么? 可能是苹果(Apple),可能是香蕉(Banana),但肯定是水果。
  • 你能做什么(读)? 因为你知道最差它也是个水果,所以你可以放心地从柜子里拿出一个东西,把它当作水果Fruit)看待。
4. 下界通配符 <? super T>

定义:表示类型是 TT 的超类(父类)。

形象解释:可写入的收集箱

你有一个 List<? super Apple>,这相当于一个收集苹果的箱子

  • 里面有什么? 这个箱子可能是 List<Apple>,也可能是 List<Fruit>,甚至是 List<Object>。具体是什么不确定,但肯定能装苹果。
  • 你能做什么(写)? 既然这个箱子是用来装苹果的,那你放心地往里扔苹果Apple)肯定没错。 扔青苹果(GreenApple,Apple的子类)也行。

  • 泛型类Box<T> 就像一条专门生产某类玩具的生产线。如果你启动了 Box<Car> 生产线,这条线上的机械臂(实例方法)都知道自己抓取的是 Car
  • 泛型静态方法<U> Box<U> createBox(U item) 就像工厂里的万能启动钥匙或者一条灵活的小型工作台。 你给它一块木头(Wood),它就启动一个专门生产木头盒子的生产线(Box<Wood>)。 你给它一块铁(Iron),它就启动一个专门生产铁盒子的生产线(Box<Iron>)。 这个启动方法不依赖于任何一条具体的生产线,它自己就能根据原料决定启动哪条线。

  • Collections 工具类Collections.emptyList() 返回一个空的 List<T>
  • 工厂方法:像上面例子那样,提供一种更优雅的创建对象的方式。
  • 算法工具:比如一个静态的排序方法,可以处理任何类型的数组,只要你能提供一个比较器。

总结一下

  1. 通配符 (?):解决的是“我要用一段代码处理多种泛型类型”的问题。
    • ? extends T只能读,不能写(除非 null)。适合生产者(读取数据)。
    • ? super T只能写,读只能当 Object。适合消费者(写入数据)。
    • PECS 原则Producer Extends, Consumer Super(生产者使用 Extends,消费者使用 Super)。
  2. 泛型静态方法:解决的是“静态上下文无法使用类的泛型参数”的问题。
    • 它独立于泛型类,拥有自己独立的类型参数。
    • 常用于编写通用的工具方法(如各种算法)。

结语:如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 通配符
    • 1. 为什么要用通配符?
    • 2. 形象解释:仓库管理员
    • 3. 上界通配符 <? extends T>
    • 4. 下界通配符 <? super T>
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档