首页 > 技术文章 > Java8新特性-函数式接口

pain-first 2019-09-06 17:35 原文

这一节学一下用得比较多的三个函数式接口:
--Predicate
--Consumer
--Function

Predicate

java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。代码:

public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> results = new ArrayList<>();
    for(T s: list){
        if(p.test(s)){
            results.add(s);
        }
    }
    return results;
}

List<String> listOfStrings = new ArrayList<>();
listOfStrings.add("");
listOfStrings.add("a");
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
nonEmpty.forEach(System.out::println);

剩余两个函数就不一一举例了。

Consumer

java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。

Function

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。

拓展: 原始类型特化
在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing)

装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。

Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。比如,在下面的代码中,使用IntPredicate就避免了对值1000进行装箱操作,但要是用Predicate就会把参数1000装箱到一个Integer对象中:

IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000); // 无装箱
Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;
oddNumbers.test(1000); // 有装箱

ps:
1.泛型(比如Consumer中的T)只能绑定到引用类型。这是由泛型内部的实现方式造成的。
2.IntPredicate没有继承Predicate接口。

推荐阅读