基本类型专用函数式接口(如IntConsumer、LongSupplier)可避免自动装箱拆箱,减少对象创建与GC压力;其方法签名直接使用原始类型,实现栈上值传递,零包装开销。

直接用 IntConsumer、LongSupplier 这类高级函数式接口,核心目的就是绕过 Integer、Long 的自动装箱/拆箱——不创建包装对象,不走 valueOf() 或 longValue(),从源头节省内存和 CPU。
为什么基本类型专用接口能规避装箱
普通接口如 Consumer<Integer> 要求参数是引用类型,传 int 时 JVM 必须自动装箱为 Integer;而 IntConsumer 的 accept(int value) 方法签名直接声明接收原始 int,JVM 按值传递,栈上操作,零对象开销。
-
IntConsumer→ 参数是int,无返回 -
LongSupplier→ 无参数,返回long -
DoublePredicate→ 接收double,返回boolean - 所有这类接口都标注了
@FunctionalInterface,支持 Lambda 和方法引用
典型场景与写法对比
看两组常见操作,左边是“踩坑写法”,右边是“优化写法”:
- 遍历 int 数组并打印:
❌Arrays.stream(nums).boxed().forEach(System.out::println);(先装箱成Integer再打印)
✅Arrays.stream(nums).forEach(System.out::println);(nums是int[],底层是IntStream,自动匹配IntConsumer) - 生成随机 long 值:
❌Supplier<Long> bad = () -> System.nanoTime();(每次返回新Long对象)
✅LongSupplier good = System::nanoTime;(直接返回原始long,无构造开销) - 过滤正整数:
❌list.stream().filter(i -> i > 0).collect(...);(list是List<Integer>,每个i都要拆箱)
✅ 先转为原始流:list.stream().mapToInt(Integer::intValue).filter(i -> i > 0).toArray();(全程int运算)
组合与复用技巧
这些接口也支持默认方法组合,逻辑清晰且不引入装箱:
-
IntPredicate isEven = i -> i % 2 == 0;IntPredicate isPositive = i -> i > 0;IntPredicate both = isEven.and(isPositive); // 返回新 IntPredicate,仍只操作 int -
IntConsumer logAndSave = i -> { logger.info("Got: {}", i); db.save(i); };
可安全用于高频率回调,不会因重复调用产生大量Integer实例
注意事项
不是加个 “Int” 前缀就万事大吉,关键在数据源头是否保持原始类型:
- 集合仍用
List<Integer>?那stream()还是Stream<Integer>,必须先mapToInt才能接入IntConsumer - 避免混用:别把
IntConsumer强转给Consumer<Integer>,编译不通过,说明设计意图已被打破 - 第三方库若只接受泛型接口(如某些旧版框架),优先考虑升级或封装适配器,而非妥协用包装类

















