java8特性: 常见的4个jdk内置函数式接口Consumer、Supplier、Function和Predicate

88 阅读4分钟

基本概述

jdk1.8内置了许多函数式接口供以使用, 都放在java.util.function包下, 常用的基本上是Consumer、Supplier、Function和Predicate四个函数式接口, 其余也并无太大区别

接口名抽象方法功能描述
Consumervoid accpet(T t)消费型接口:将传递进来的对象t作为消费对象,对其进行逻辑处理
SupplierT get()生产型接口:通过对一个无参方法返回一个实例对象
FunctionR apply(T t)函数型接口:对T类型的对象进行逻辑处理,转换成为R类型(其他类型)返回
Predicateboolean test(T t )断言型接口:对参数传的对象进行逻辑判断,返回一个boolean类型的判断结果

1、Consume:消费型接口

将传递进来的参数对象作为消费对象, 对其进行逻辑处理

     String food = "肉";
​
        // 在调用eat()方法时用lambda表达式写出Consumer的消费逻辑
        eat(food, s -> {
            if (s.equals("肉")) {
                System.out.println("今天开荤了!");
            }
            if (s.equals("菜")) {
                System.out.println("今天又是斋戒的一天!");
            }
        });
    }
​
    // 提供一个参数中有Consumer类型的方法
    public static <T> void eat(T t, Consumer<T> consumer) {
        consumer.accept(t);
    }
}
​

使用场景:遍历集合

集合Collection接口继承了一个迭代器接口Iterable, 而Iterable接口中提供了一个用于遍历集合的forEach方法, 方法参数就是Consumer接口, 截图如下:

List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("孙七");
​
// 遍历List集合
list.forEach(name -> System.out.println(name));
//集合中forEach的源码
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

2、Supplier:生产型接口

通过一个无参方法返回一个实例

  // 在调用getString()方法时写出Supplier的生产逻辑
        String string = getString(() -> "我是生产出来的String类型的对象");
​
        System.out.println(string); //我是生产出来的String类型的对象
    }
​
    // 提供一个参数中有Supplier类型的方法
    public static String getString(Supplier<String> supplier) {
        String s = supplier.get();
        return s;
    }

使用场景:Objects.requireNonNull()对象为空时提供空指针异常的错误信息 jdk提供的对象操作工具类Objects中有一个requireNonNull()方法, 该方法的作用是用于判断对象是否为空, 如果为空, 则抛出一个空指针异常, 并指定异常信息,

public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}
String b=null;
String s = Objects.requireNonNull(b,()->"你给的数据是空的");
System.out.println(s);
//Exception in thread "main" java.lang.NullPointerException: 你给的数据是空的

3、Function:函数型接口

对T类型的对象进行逻辑处理,转换成为R类型(其他类型)的对象

    String str=getString(99,i->i.toString());
    System.out.println(str+111);  //99111
​
}
public static  String getString(Integer integer,Function<Integer,String> function){
    String apply=function.apply(integer);
    return apply;
}
应用场景: Optional中通过map()方法转换元素类型

jdk1.8提供的专为空指针判断而诞生的一个工具类Optional中, 存在一个map()方法, 用于转换Optional中的元素类型, map方法中的其中一个参数就是Function

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }  //源码
}

基本用法:

// 根据id查询学生对象
    Student student = getStuById(123456);
​
    // map方法将元素换为另一种类型的元素(此处将Student类型转换为Boolean类型)
    Optional<Boolean> optional = Optional.ofNullable(student).map(stu -> {
        if (stu.getName().equals("张三")) {
            return true;
        } else {
            return false;
        }
    });
}

对于Option类的使用可以参考我的这篇文章,直接速解疑惑JDK8新特性-Optional类 - 掘金 (juejin.cn)

4、Predicate:断言型接口

对参数传的对象进行逻辑判断, 返回一个boolean类型的判断结果

     // 如果传入的字符串是西瓜, 就返回true, 不是西瓜就false
        boolean pre = predicateTest("苹果", s -> "西瓜".equals(s));
        System.out.println(pre);
    }
    // 提供一个参数中有Predicate类型的方法
    public static boolean predicateTest(String s, Predicate<String> predicate) {
        return predicate.test(s);
    }
}

应用场景:Collection集合中的满足条件就删除元素的方法

集合Collection接口中存在一个removeIf()方法,方法作用就是将集合中的元素按照指定的判断规则移除,参数就是Predicate接口;

//源码
 default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            //判断是否符合给定的判断规则的要求,满足就直接移除
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

基本使用:

           List<String> list = new ArrayList<>();
            list.add("张三");
            list.add("李四");
            list.add("王五");
            list.add("赵六");
            list.add("张三2");
            list.add("张三3");
            list.add("张三4");
            // 如果存在以"张三"开头的元素, 则从元素中移除
            list.removeIf(name -> name.startsWith("张三"));
            // 遍历List集合
            list.forEach(name -> System.out.println(name));
//        李四
//        王五
//        赵六

总结

函数式接口在代码中运用非常广泛,特别是在Stream或者Optional类中使用时最多的,在配合Lambda表达式,可以让我们在实际开发中的代码更加简洁优雅。最常见的函数式接口几乎就是本文中提到的四种,虽然不止这些,但是处处都是这几个函数,掌握这几个函数后可以如履平地。