java8新特性之内置函数式接口

Posted by Chris on August 19, 2019

前面介绍过函数式接口,函数式接口可以接受lambda表达式,lambda表达式代表一个操作。对于一些比较通用的操作,例如,传入两个参数,得到一个返回结果,或者传入一个参数,没有返回结果,java8已经提供了内置的函数式接口。下面我们就看看这些内置的函数式接口。

1、java8内置的函数式接口

Function

apply()方法接收一个参数,返回一个结果。方法声明如下所示。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

下面看一个例子。例子中,Map.computeIfAbsent(K key, Function mappingFunction)接收两个参数。如果key存在且有对应的值,则返回对应的值;如果key不存在或者没有对应的值,则使用mappingFunction对key做映射,返回映射结果。例子中,由于”Chris”不存在,因此,返回的结果为”no addr”。

Map<String, String> nameAddrMap = new HashMap<>();
String addr = nameAddrMap.computeIfAbsent("Chris", name -> "no addr");

考虑到基本类型的情况(不是包装类型),为了方便基本类型的使用,内置的对于基本类型的支持有如下这些:

1)IntFunction, LongFunction, DoubleFunction:参数为对应的基本类型int, long, double,返回结果为泛型。例如,

@FunctionalInterface
public interface DoubleFunction<R> {
    R apply(double value);
}

2)ToIntFunction, ToLongFunction, ToDoubleFunction:参数为泛型,返回结果为基本类型int, long, double。例如,

@FunctionalInterface
public interface ToDoubleFunction<T> {
    double applyAsDouble(T value);
}

3)DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction, LongToIntFunction, LongToDoubleFunction:参数为基本类型,返回结果为也是基本类型。例如,

@FunctionalInterface
public interface LongToDoubleFunction {
    double applyAsDouble(long value);
}

BiFunction

接收两个参数,返回一个结果。方法声明如下

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

看看下面的例子。map.replaceAll(BiFunction function)接收一个BiFunction类型的参数。因此,function必须是有两个输入参数,一个输出结果的lambda表达式。代码中,将名字为”Chris”的人工资涨5%。

Map<String, Double> nameSalaryMap = new HashMap<>();
nameSalaryMap.put("Chris", 1000d);
nameSalaryMap.put("David", 2000d);
nameSalaryMap.replaceAll((name, salary) -> "Chris".equals(name) ? salary * 1.05: salary);

BiFunction也提供了对于基本类型的支持。ToDoubleBiFunction, ToIntBiFunction, and ToLongBiFunction返回的结果类型分别为double, int, long。

Supplier

不接收参数,返回一个结果。方法声明如下:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

下面的例子中,supplier为一个随机函数的生成器。每次调用get()方法都会产生一个随机数。

Supplier<Integer> supplier = () -> RandomUtils.nextInt(0, 10);
for (int i = 0; i < 100; i++) {
  System.out.println(supplier.get());
}

Consumer

接收一个参数,不返回任何结果。方法声明如下。Consumer的作用一般是产生某个副作用,不返回执行结果。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

下面的例子中,Consumer的副作用就是打印出列表中的元素。

Consumer<String> sideEffect = name -> System.out.println(name);
Lists.newArrayList("Chris", "David", "Bob").forEach(sideEffect);

Consumer有对应的BiConsumer,作用是接收两个参数,不返回任何结果。

Predicate

接收一个参数,返回一个boolean类型的结果。声明如下。如果lambda表达式对于参数t评估为真,则返回true;否则,返回false。

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

下面的例子中,对于字符串长度小于等于3的元素,会被过滤掉。

ArrayList<String> strings = Lists.newArrayList("Chris", "David", "Bob");
strings.stream().filter(s -> s.length() > 3).collect(Collectors.toList());

UnaryOperator

接收一个参数,返回一个同类型的参数。继承了Function接口。声明如下,

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
   T apply(T t);
}

UnaryOperator可以认为是Function的特殊场景。

下面的代码中,将所有的元素都转化为大写。

ArrayList<String> strings = Lists.newArrayList("Chris", "David", "Bob");
strings.replaceAll(name -> name.toUpperCase());

类似的,BinaryOperator继承了BiFunction,接收两个同类型的参数,返回一个同类型的结果。

java8之前提供的函数式接口

2、总结

java8内置的函数式接口主要是一些比较通用的函数式接口,大致可以划分如下:

3、参考

https://www.baeldung.com/java-8-functional-interfaces