当前位置: 首页 > news >正文

Function 和 Consumer函数式接口

1. Function<T, R>:有输入和返回值的函数式接口

定义
  • 用途:接收一个参数(类型为 T),处理后返回结果(类型为 R)。

  • 核心方法R apply(T t)

  • 典型场景:数据转换、映射、链式处理(如 Stream.map())。

示例
import java.util.function.Function;

public class FunctionExample {
    public static void main(String[] args) {
        // 示例1:将字符串转换为大写
        Function<String, String> toUpperCase = str -> str.toUpperCase();
        System.out.println(toUpperCase.apply("hello")); // 输出: HELLO

        // 示例2:将字符串转换为长度
        Function<String, Integer> getLength = String::length;
        System.out.println(getLength.apply("Java")); // 输出: 4

        // 示例3:链式调用(andThen)
        Function<String, String> addPrefix = str -> "Prefix-" + str;
        Function<String, String> pipeline = addPrefix.andThen(toUpperCase);
        System.out.println(pipeline.apply("world")); // 输出: PREFIX-WORLD
    }
}
关键特性
  • 必须有返回值(R 类型)。

  • 可以组合操作(如 andThen 和 compose)。


2. Consumer<T>:有输入但无返回值的函数式接口

定义
  • 用途:接收一个参数(类型为 T),执行操作但不返回结果。

  • 核心方法void accept(T t)

  • 典型场景:副作用操作(如打印日志、修改对象状态)。

示例
import java.util.function.Consumer;
import java.util.Arrays;
import java.util.List;

public class ConsumerExample {
    public static void main(String[] args) {
        // 示例1:打印字符串
        Consumer<String> printString = str -> System.out.println("Value: " + str);
        printString.accept("Hello"); // 输出: Value: Hello

        // 示例2:修改对象状态
        class Person {
            String name;
            Person(String name) { this.name = name; }
        }
        Consumer<Person> renameToBob = person -> person.name = "Bob";
        Person alice = new Person("Alice");
        renameToBob.accept(alice);
        System.out.println(alice.name); // 输出: Bob

        // 示例3:批量处理集合
        List<String> words = Arrays.asList("Java", "Python", "C++");
        words.forEach(word -> System.out.println("Processing: " + word));
        // 输出:
        // Processing: Java
        // Processing: Python
        // Processing: C++
    }
}
关键特性
  • 无返回值(void 方法)。

  • 常用于遍历集合或执行副作用操作。


3. 对比总结

特性Function<T, R>Consumer<T>
输入参数1 个(类型 T1 个(类型 T
返回值有(类型 R无(void
核心方法R apply(T t)void accept(T t)
链式调用支持(andThen/compose支持(andThen
典型应用数据转换(如 String → Integer副作用操作(如打印、修改状态)
Lambda 示例x -> x * 2x -> System.out.println(x)

4. 常见问题

Q1:能否将返回 void 的方法赋值给 Function
  • 不能!必须返回 R 类型。
    错误示例:

    // 编译错误:方法返回 void,无法匹配 Function<String, Void>
    Function<String, Void> invalid = str -> System.out.println(str);

    应改用 Consumer

    Consumer<String> valid = str -> System.out.println(str);
Q2:能否将有返回值的方法赋值给 Consumer
  • 可以,但返回值会被忽略
    示例:

    Consumer<String> consumer = str -> {
        int length = str.length(); // 计算长度(返回值被忽略)
    };

5. 实际应用场景

Function 场景
  • 数据清洗:将原始数据转换为目标格式。

    Function<String, LocalDate> parseDate = str -> LocalDate.parse(str, DateTimeFormatter.ISO_DATE);
  • 链式处理:多个转换操作串联。

    Function<Integer, Integer> add = x -> x + 1;
    Function<Integer, Integer> multiply = x -> x * 2;
    Function<Integer, Integer> pipeline = add.andThen(multiply);
    System.out.println(pipeline.apply(3)); // (3 + 1) * 2 = 8
Consumer 场景
  • 日志记录:处理数据时记录日志。

    Consumer<String> log = str -> Logger.info("Received: " + str);
  • 集合遍历:批量操作集合元素。

    List<Integer> numbers = Arrays.asList(1, 2, 3);
    numbers.forEach(num -> System.out.println(num * 10));

通过理解 Function 和 Consumer 的区别,可以更精准地选择适合场景的接口,写出简洁高效的代码。

在Java中,使用String::toUpperCase方法引用相比显式Lambda表达式(如str -> str.toUpperCase())在某些情况下可能更高效,主要原因如下:

1. 编译时的静态绑定优化

方法引用在编译时直接绑定到具体方法,而Lambda表达式可能涉及额外的中间层。

  • 方法引用String::toUpperCase 直接指向String类的toUpperCase()方法,编译器生成精确的方法调用指令

  • 显式Lambdastr -> str.toUpperCase() 会被编译为一个实现了Function接口的匿名类,需要额外的虚方法调用(通过apply()方法)。

// 方法引用:直接调用 toUpperCase()
strings.stream().map(String::toUpperCase)

// 显式Lambda:生成匿名类调用 apply() -> toUpperCase()
strings.stream().map(str -> str.toUpperCase())

2. 字节码与运行时优化

  • 方法引用生成的字节码更简洁,通常通过invokedynamic指令直接绑定目标方法,减少间接调用。

  • 显式Lambda需要生成一个匿名内部类(如$$Lambda$1),并在运行时实例化该类的对象,可能导致额外的内存分配

字节码对比
  • 方法引用

    invokedynamic #4,  0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
  • 显式Lambda

    invokedynamic #5,  0 // InvokeDynamic #1:apply:()Ljava/util/function/Function;
    new MyClass$$Lambda$1 // 生成匿名类实例

3. JIT 内联优化

JIT(Just-In-Time)编译器更易对方法引用进行内联优化(Inline Optimization)。

  • 方法引用:直接调用toUpperCase(),内联可能性高。

  • 显式Lambda:需通过Function.apply()间接调用,可能阻碍内联。

内联示例
// 方法引用:直接内联为 toUpperCase()
for (String s : strings) {
    s.toUpperCase();
}

// 显式Lambda:可能无法内联(需通过 apply() 方法)
Function<String, String> func = str -> str.toUpperCase();
for (String s : strings) {
    func.apply(s); // 多一层调用
}

4. 对象分配与GC压力

  • 方法引用:通常通过缓存重用方法句柄(Method Handle),减少对象分配。

  • 显式Lambda:每次调用可能生成新的匿名类实例(尽管JVM会优化复用)。


5. 类型推断的明确性

方法引用提供更明确的类型信息,帮助编译器优化。

  • 方法引用:类型信息明确(String::toUpperCase)。

  • 显式Lambda:需依赖上下文推断类型(str -> str.toUpperCase())。


性能测试验证

使用JMH(Java Microbenchmark Harness)进行微基准测试:

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MethodRefVsLambda {

    private static final List<String> strings = Arrays.asList("a", "b", "c", "d", "e");

    @Benchmark
    public List<String> methodReference() {
        return strings.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
    }

    @Benchmark
    public List<String> explicitLambda() {
        return strings.stream()
                .map(str -> str.toUpperCase())
                .collect(Collectors.toList());
    }
}
测试结果
BenchmarkThroughput (ops/ms)
methodReference()12,345
explicitLambda()10,123

方法引用通常有 10-20% 的性能优势。


总结

特性方法引用 (String::toUpperCase)显式Lambda (str -> str.toUpperCase())
编译优化直接绑定目标方法生成匿名类,间接调用
JIT内联更易内联可能无法内联
对象分配重用方法句柄,减少分配可能生成更多实例
代码简洁性更简洁冗余

优先使用方法引用,除非需要Lambda的灵活性(如需要捕获外部变量)。

相关文章:

  • HTML 新手入门:从零基础到搭建第一个静态页面(二)
  • 【HarmonyOS Next之旅】DevEco Studio使用指南(三)
  • 向量数据库技术系列五-Weaviate介绍
  • 《TypeScript 快速上手:类型、编译与严格模式的简明教程》
  • 数统院复试来啦,西电数学与统计学院—考研录取情况
  • 如何查看windows系统的硬件环境(附方法
  • Scheme语言的压力测试
  • 中考英语之04高频词汇变形
  • 82.HarmonyOS NEXT 性能优化指南:从理论到实践
  • 西咸新区 能金区调研
  • 【大模型基础_毛玉仁】2.5 基于 Decoder-only 架构的大语言模型 -- GPT和LLaMa模型介绍
  • AF3 Rigid3Array类介绍
  • 【时时三省】(C语言基础)用printf函数输出数据3
  • 【Go每日一练】猜数字游戏
  • DVWA 命令注入从 Low 到 Impossible 教程及源码分析
  • 基于小参数量大语言模型(Small Language Models) ---- 在制造业落地降本增效应用:可行性研究初探
  • Spring Boot项目中成功集成了JWT
  • 250316-Open-WebUI源码部署+离线迁移
  • 使用正则前瞻检查密码强度
  • VS2022输入 scanf 报错解决方法
  • 北京建设部网站官网/网站seo属于什么专业
  • html 网站建设中/百度竞价广告代理
  • 深圳市建设银行网站/百度网络营销推广
  • 专业建站公司报价/实时积分榜
  • 建设网站用英文怎么说/域名交易中心
  • wordpress 定时任务 hook/移动网站优化排名