Java中Function接口接收子接口类型参数的方法与示例
要让一个接受 Function 的方法也能安全接收 Function(其中 B extends A),需将参数声明为 Function super B, String>,这正是 PECS 原则在逆变场景下的典型应用,从而实现类型安全的协变兼容。
先说结论:在 Java 泛型的世界里,Function 和 Function 之间并不是父子关系——哪怕 B 确实继承了 A,这两个函数类型也是不变的(invariant)。这意味着你无法直接把 Function 传给一个要求 Function 的变量——编译器会直接拒绝,因为类型不安全。
问题的本质在于:你真正想要的是把一个 B 的实例传入函数,而这个函数需要能够处理 B 或者 B 的任意父类型(比如 A)。此时就该让逆变(contravariance)出场了:使用 ? super B 来表示“这个函数能接受 B 或者 B 的任意超类”。简单来说,就是声明为 Function super B, String>。
那么,具体如何实现?看一段实际代码就清楚了:
// 接口定义
public interface Displayable {}
public interface Purchasable extends Displayable {}
// 构造器或方法签名(关键!)
public class DisplayablePanel {
private final Function super Purchasable, String> displayTextFn;
public DisplayablePanel(Function super Purchasable, String> fn) {
this.displayTextFn = fn;
}
public void render(Purchasable item) {
String label = displayTextFn.apply(item); // ✅ 安全调用
// ... 使用 label 创建 UI 元素
}
}
这样设计之后,下面几种函数都能顺利传入——宽松程度各不相同,但全部合法:
// ✅ 接受 Purchasable(最具体) FunctionpurchasableFn = p -> p.getName() + " ($" + p.getPrice() + ")"; // ✅ 接受 Displayable(更宽泛,仍兼容) Function displayableFn = d -> d.toString(); // ✅ 甚至接受 Object(极端宽泛,但合法) Function
有几个要点值得特别提醒:
- ❌ 千万别用 Function extends A, String>——方向完全反了。? extends A 表示“返回 A 的子类型”,但 apply() 方法需要的是输入参数。对输入来说,? extends A 是只读不安全的,无法保证你传入的 B 能被这个函数接受。
- ✅ ? super B 遵循的是经典的 PECS 原则(Producer Extends, Consumer Super)。注意,Function 的第一个类型参数是消费者——它消费输入对象,所以用 super 才对。
- 这个方案完全不会破坏类型安全。道理很简单:B 是 A 的子类型,任何能处理 B 的函数自然能处理 B 的实例;而 ? super B 允许函数处理 B、A 甚至 Object 这些更宽泛的类型,恰好满足“所有 Purchasable 都是 Displayable”这个设计意图。
一句话总结:当你需要函数参数支持子类型的实参时,不要试图把函数本身向上转型——那条路不通。正确的做法是通过 ? super SubType 来重新定义函数类型边界。这才是 Java 泛型中实现既安全又灵活的接口适配的正确方式。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
列表遍历时动态判断阈值并返回相应文本
遍历数值列表时,先筛选满足阈值的元素,再根据结果输出列表或友好提示。推荐使用列表推导式结合条件判断,注意边界用`>=`,空列表自动为假。也可用`any()`提前终止遍历,提升效率。此法简洁,避免显式循环,特别适合阈值筛选。
Maven项目中如何强制使用本地构建的依赖版本
多模块开发中强制使用本地依赖的正确做法是使用-SNAPSHOT版本并配合`-U`参数强制更新,或重构为多模块项目统一管理生命周期。避免使用版本范围语法或手动复制JAR,确保构建行为可靠可重复。
Go语言net.Conn并发写安全与原子性保障解析
Go标准库中net Conn支持并发方法调用,但Write()不保证原子性。多goroutine同时写入时,系统可能拆分数据包,导致内容交错,破坏消息边界。必须使用互斥锁或bufio Writer等显式同步机制确保写操作完整性,不可依赖系统调用本身的原子性。
Python在Windows系统中获取指定卷标U盘驱动器字母的方法
使用Pythonwmi库通过Win32_Volume接口查询Windows系统中卷标为“TOSHIBA”的U盘盘符。需安装wmi和pywin32,注意大小写区分及多设备过滤。仅适用于Windows。
TP6.0消息已读功能基于Redis Bitmap未读计数方案
TP6 0中使用Redis位图实现消息已读标记,每条消息仅占一个比特,内存效率高。需将消息ID映射为连续偏移量,通过SETBIT和GETBIT操作。需提前维护用户ID到偏移量的映射表,注意键过期与驱动类型(phpredis与predis)问题。此方法内存极省,适合海量消息场景,且需确保偏移量唯一。
- 日榜
- 周榜
- 月榜
相关攻略
2026-07-08 06:50
2026-07-08 06:50
2026-07-08 06:50
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

