java.util.function包是JDK 8中的一个新包,包含40多个预定义的函数式接口,这些接口使编写Lambda表达式更容易。下表给出了一些预定义函数式接口。

函数式接口 说明
Function 为一个函数建模,该函数接收一个参数并返回一个结果。结果类型可不同于任何参数类型
BiFunction 为一个函数建模,该函数接收两个参数并返回一个结果。结果类型可不同于任何参数类型
UnaryOperator 表示一个操作数上的运算,该操作数返回的结果的类型与操作数的类型相同。可将UnaryOperator看作返回值与参数类型相同的Function。事实上,UnaryOperatorFunction的子接口
BinaryOperator 表示两个操作数上的运算,返回结果必须和操作数的类型相同
Predicate 一个函数,它接收一个参数并根据该参数的值返回truefalse
Supplier 表示结果的提供者
Consumer 一种操作,它接收一个参数,但不返回任何结果

Function-接收1个参数并返回一个结果

image-20210216192223103

<T> –函数输入的类型
<R> –函数结果的类型,结果类型可不同于参数类型

Function-例1

public static void main( String[] args )
{
    Function<Integer, Double> milesToKms =
            (input) -> 1.6 * input;
    int miles = 3;
    double kms = milesToKms.apply(miles);
    System.out.printf("%d miles = %3.2f kilometers\n",
            miles, kms);
}

Function-例2-用方法作为参数

/**
 * 转换功能
 * @param convertFunction 转换函数
 * @param inputData       输入数据
 * @param <INPUT_TYPE>
 * @param <RETURN_TYPE>
 * @return
 */
public static <INPUT_TYPE, RETURN_TYPE> RETURN_TYPE convert(Function<INPUT_TYPE, RETURN_TYPE> convertFunction, INPUT_TYPE inputData){
    return convertFunction.apply(inputData);
}

将方法作为参数:

Double milesToKmsResult = convert((input) -> 1.6 * input, 3);

比如:

public static void main( String[] args )
{
    int miles = 3;
    double kms = convert((input) -> 1.6 * input, 3);
    System.out.printf("%d miles = %3.2f kilometers\n",
            miles, kms);
}

BiFunction-接收2个参数并返回一个结果

image-20210216215813534

为一个函数建模,该函数接收两个参数并返回一个结果。结果类型可不同于任何参数类型

BiFunction<Float, Float, Float> area = 
(width, length) -> width * length;
float width = 7.0F;
float length = 10.0F;
float result = area.apply(width, length);
System.out.println(result);

UnaryOperator-一个参数一个返回类型相同

表示一个操作数上的运算,该操作数返回的结果的类型与操作数的类型相同。可将UnaryOperator看作返回值与参数类型相同的Function。事实上,UnaryOperatorFunction的子接口。

image-20210216220456374

BinaryOperator-两个参数一个返回类型都相同

表示两个同类型操作数上的运算,返回结果必须和操作数的类型相同

image-20210216220905585

Predicate-接收一个参数返回布尔值

image-20210216221131551

例子:定义一个Predicate,用于计算输入字符串,如果字符串中的每个字符都是数字,则返回true

Predicate<String> numbersOnly = (input) -> {
    for (int i = 0; i < input.length(); i++) {
        char c = input.charAt(i);
        if ("0123456789".indexOf(c) == -1) {
            return false;
        }
    }
    return true;
};

System.out.println(numbersOnly.test("12345"));   // true
System.out.println(numbersOnly.test("100a"));    // false

Supplier-表示结果的提供者

image-20210217094819630

例一:

private static String getString(Supplier<String> stringSupplier) {
	return stringSupplier.get();
}
public static void main( String[] args )
{
    String msgA = "Hello ";
    String msgB = "World ";
    System.out.println(
            getString(() -> msgA + msgB)
    );
}

image-20210217095157873

例二:定义一个Supplier,用于返回一个随机数(一位数),并使用for循环打印5个随机数。

Supplier<Integer> oneDigitRandom = () -> {
    Random random = new Random();
    return random.nextInt(10);
};
for (int i = 0; i < 5; i++) {
    System.out.println(
            oneDigitRandom.get());
}

Supplier也有专门的变体,如DoubleSupplier(返回一个Double)、IntSupplierLongSupplier等。

Consumer-接收一个参数,但不返回任何结果

image-20210217095433235

accepts a single input argument and returns no result

接收一个参数,但不返回任何结果

例子一:打印水果列表

在jdk内对Consumer的典型使用:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

有一个需要打印的水果列表,可以通过调用forEach方法来实现这一点

List<String> fruits = Arrays.asList("Apple", "Banana");
// 使用Lambda表达式
fruits.forEach((name) -> System.out.println(name));

另外,因为System.out是系统创建的一个现有对象,所以可以使用对System.outprintln方法的方法引用:

fruits.forEach(System.out::println);

例子二:接收一个字符串并将其以中心对齐方式打印出来。

// 辅助用
Function<Integer, String> spacer = (count) -> {
    StringBuilder sb = new StringBuilder(count);
    for (int i = 0; i < count; i++) {
        sb.append(" ");
    }
    return sb.toString();
};
int lineLength = 60;    // 每行的字符数
Consumer<String> printCentered = 
        (input) -> {
            int length = input.length();
            String spaces = spacer.apply(
                    (lineLength - length) / 2);
            System.out.println(spaces + input);
        };
printCentered.accept("A lambda expression a day");
printCentered.accept("makes you")
printCentered.accept("look smarter");

这个Consumer,它接收一个字符串,并在该字符串前面加上一定数量的空格后打印它。每一行的最大字符数为60,通过调用一个名为spacer的Function来获得空格。Consumer的accept方法的实现由下面的Lambda表达式给出:

(input) -> {
    int length = input.length();
    String spaces = spacer.apply(
            (lineLength - length) / 2);
    System.out.println(spaces + input);
}

函数spacer返回指定的空格数,其定义为:

Function<Integer, String> spacer = (count) -> {
    StringBuilder sb = new StringBuilder(count);
    for (int i = 0; i < count; i++) {
        sb.append(" ");
    }
    return sb.toString();
};

该函数使用了一个for循环,该循环每次向StringBuilder上添加一个空格,共添加count个空格,其中count是函数的参数。当循环退出时,该函数返回StringBuilder的字符串表示形式。

运行后控制台上将输出下面的内容:

                 A lambda expression a day
                         makes you
                        look smarter

另有BiConsumer接口类,该类与Consumer的区别是可以接受2个参数。

相关接口类有:

类名 描述
IntConsumer 接受单个int型参数的Consumer操作
DoubleConsumer 接受单个double型参数的Consumer操作
LongConsumer 接受单个long型参数的Consumer操作
ObjIntConsumer 接受2个int型参数的Consumer操作,不支持andThen方法
ObjDoubleConsumer 接受2个double型参数的Consumer操作,不支持andThen方法
ObjLongConsumer 接受2个long型参数的Consumer操作,不支持andThen方法