ZetCode

Java Function接口

最后修改时间:2025 年 4 月 16 日

java.util.function.Function接口代表一个接受一个参数并产生结果的函数。它是一个函数式接口,只有一个抽象方法apply。Function通常用于转换流操作中的数据和方法引用。

Function是Java 8中添加的Java函数式编程实用程序的一部分。它支持行为参数化,并有助于编写更简洁的代码。该接口提供了用于函数组合和链式调用的默认方法。

Function接口概述

Function接口包含一个抽象方法和几个默认方法。关键方法apply对输入执行操作。其他方法支持函数组合和转换链。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
    
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before);
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after);
    static <T> Function<T, T> identity();
}

上面的代码显示了Function接口的结构。它使用泛型,其中T是输入类型,R是结果类型。该接口使用@FunctionalInterface注解进行标注,以表明其单一抽象方法的性质。

Function的基本用法

使用Function的最简单方法是使用lambda表达式。我们在apply方法中定义如何将输入转换为输出。此示例将字符串转换为它们的长度。

Main.java
package com.zetcode;

import java.util.function.Function;

public class Main {

    public static void main(String[] args) {

        // Define a function that takes String and returns its length
        Function<String, Integer> lengthFunction = s -> s.length();
        
        // Apply the function
        System.out.println("Length of 'hello': " + lengthFunction.apply("hello"));
        System.out.println("Length of 'functional': " + lengthFunction.apply("functional"));
        
        // Function using method reference
        Function<String, Integer> lengthMethodRef = String::length;
        System.out.println("Length via method ref: " + lengthMethodRef.apply("method"));
    }
}

此示例演示了lambda和方法引用的基本Function用法。lengthFunction接受String并返回Integer。我们将其应用于不同的字符串。方法引用为现有方法提供了更简洁的语法。

使用 andThen 进行函数组合

andThen方法允许链接函数,其中一个函数的输出成为下一个函数的输入。这使得可以从简单的函数创建复杂的转换。

Main.java
package com.zetcode;

import java.util.function.Function;

public class Main {

    public static void main(String[] args) {

        // First function converts String to uppercase
        Function<String, String> toUpper = s -> s.toUpperCase();
        
        // Second function extracts first 3 characters
        Function<String, String> firstThree = s -> s.substring(0, Math.min(s.length(), 3));
        
        // Compose the functions
        Function<String, String> upperThenTrim = toUpper.andThen(firstThree);
        
        System.out.println("Result: " + upperThenTrim.apply("hello world"));
        System.out.println("Result: " + upperThenTrim.apply("java"));
    }
}

此示例展示了使用andThen的函数组合。输入字符串首先被转换为大写,然后被修剪为前3个字符。操作顺序在链中从左到右。

使用 compose 进行函数组合

compose方法类似于andThen,但以相反的顺序执行函数。参数函数首先运行,然后是原始函数。

Main.java
package com.zetcode;

import java.util.function.Function;

public class Main {

    public static void main(String[] args) {

        // Function to double a number
        Function<Integer, Integer> doubler = x -> x * 2;
        
        // Function to increment by 1
        Function<Integer, Integer> incrementer = x -> x + 1;
        
        // Compose in different orders
        Function<Integer, Integer> incrementThenDouble = doubler.compose(incrementer);
        Function<Integer, Integer> doubleThenIncrement = doubler.andThen(incrementer);
        
        System.out.println("Increment then double 5: " + incrementThenDouble.apply(5));
        System.out.println("Double then increment 5: " + doubleThenIncrement.apply(5));
    }
}

此示例演示了composeandThen之间的区别。使用compose,增量发生在加倍之前。使用andThen,加倍发生在增量之前。结果有所不同。

将Function与Streams一起使用

Function通常与Java Streams一起用于数据转换。map操作接受一个Function来转换流元素。这使得可以创建干净的数据处理管道。

Main.java
package com.zetcode;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {

        List<String> names = Arrays.asList("alice", "bob", "charlie", "dave");
        
        // Function to capitalize first letter
        Function<String, String> capitalize = s -> 
            s.substring(0, 1).toUpperCase() + s.substring(1);
        
        // Apply function in stream
        List<String> capitalizedNames = names.stream()
            .map(capitalize)
            .collect(Collectors.toList());
            
        System.out.println("Original: " + names);
        System.out.println("Transformed: " + capitalizedNames);
    }
}

此示例显示了Streams中的Function用法。我们定义一个大写函数,并通过map将其应用于每个流元素。结果是一个新的列表,其中包含转换后的值。流操作变得非常具有表现力。

Function Identity

Function.identity方法返回一个始终返回其输入参数的函数。当操作需要一个函数,但您希望原样传递值时,它很有用。

Main.java
package com.zetcode;

import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {

        // Identity function
        Function<String, String> identity = Function.identity();
        
        System.out.println("Identity applied: " + identity.apply("test"));
        
        // Practical use in streams
        Stream<String> words = Stream.of("a", "b", "c");
        var result = words.collect(Collectors.toMap(
            Function.identity(), // key mapper
            String::length       // value mapper
        ));
        
        System.out.println("Resulting map: " + result);
    }
}

此示例演示了Function.identity。identity函数返回其输入未更改。在流中,它通常用作占位符,当不需要转换但需要一个Function时使用。

BiFunction及其他

虽然Function接受一个参数,但Java提供了用于不同元数的相关接口。BiFunction接受两个参数,并且存在用于基本类型的专用接口以避免装箱。

Main.java
package com.zetcode;

import java.util.function.BiFunction;
import java.util.function.DoubleFunction;
import java.util.function.ToIntFunction;

public class Main {

    public static void main(String[] args) {

        // BiFunction example
        BiFunction<Integer, Integer, String> sumToString = 
            (a, b) -> String.valueOf(a + b);
        System.out.println("Sum as string: " + sumToString.apply(5, 3));
        
        // Primitive specialized functions
        DoubleFunction<String> doubleFormatter = d -> String.format("$%.2f", d);
        System.out.println("Formatted: " + doubleFormatter.apply(12.3456));
        
        ToIntFunction<String> stringToLength = String::length;
        System.out.println("Length as int: " + stringToLength.applyAsInt("hello"));
    }
}

此示例显示了Function的变体。BiFunction处理两个输入,而诸如DoubleFunction之类的基本类型特化提高了性能。Java的函数式接口涵盖了许多常见用例。

来源

Java Function接口文档

在本文中,我们介绍了Java Function接口的基本方法和特性。理解这些概念对于现代Java应用程序中的函数式编程和流处理至关重要。

作者

我叫Jan Bodnar,是一位经验丰富的程序员,在该领域拥有多年的经验。我从2007年开始撰写编程文章,此后撰写了1400多篇文章和8本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程