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

函数式接口简介

注: 本篇博客主要对Consumer、Supplier、Function和Predicate四个函数式接口的使用进行说明, 阅读本文需要lambda表达式相关知识, 如不熟悉, 可参考笔者其他博客: java8特性: lambda表达式语法入门-笔记狂魔

函数式接口: 如果一个接口中有且只有一个抽象方法(可以有其他方法比如default方法等, 但是抽象方法只能有一个),则该接口就可以称之为函数式接口

@FunctionalInterface注解: 该注解可标识当前类是一个函数式接口, 如果不满足函数式接口的条件但是却使用了此注解, 则会报错

@FunctionalInterface // 该注解可标识当前类是一个函数式接口, 如果不是, 则会报错
public interface MyFunctionalInterface {

    // 函数式接口中的唯一一个抽象方法
    void play();

    default void eat(){
        System.out.println("接口中的default方法");
    }
}

jdk1.8内置的4个函数式接口

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

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

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

(1) 基本用法
public class ConsumerTest {
    public static void main(String[] args) {

        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);
    }
}
(2) 实际运用案例: 遍历集合

集合Collection接口继承了一个迭代器接口Iterable, 而Iterable接口中提供了一个用于遍历集合的forEach方法, 方法参数就是Consumer接口, 截图如下:
Iterable接口中的forEach()方法
因此, 我们在遍历List或者Set集合时, 可以直接调用此方法进行遍历, 使得代码大大简化:

public static void main(String[] args) {

    List<String> list = new ArrayList<>();
    list.add("张三");
    list.add("李四");
    list.add("王五");
    list.add("赵六");
    list.add("孙七");

    // 遍历List集合
    list.forEach(name -> System.out.println(name));
}
2. Supplier: 生产型接口

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

(1) 基本用法
public class SupplierTest {
    public static void main(String[] args) {

        // 在调用getString()方法时写出Supplier的生产逻辑
        String string = getString(() -> "我是生产出来的String类型的对象");

        System.out.println(string);
    }

    // 提供一个参数中有Supplier类型的方法
    public static String getString(Supplier<String> supplier) {
        String s = supplier.get();
        return s;
    }
}
(2) 实际运用案例: Objects.requireNonNull()对象为空时提供空指针异常的错误信息

jdk提供的对象操作工具类Objects中有一个requireNonNull()方法, 该方法的作用是用于判断对象是否为空, 如果为空, 则抛出一个空指针异常, 并指定异常信息, 截图如下:
在这里插入图片描述
因此, 假设我们在代码中需要设定自定义的异常信息, 就可以调用此方法:

public static void main(String[] args) {

		// 根据id查询一个Student对象
        Student stu = findStudentById("123456");
        // 假如对象为空, 则抛出空指针异常, 并指定错误信息
        Objects.requireNonNull(stu,()->"自定义异常信息: 传入的Student对象为空!");
    }
3. Function: 函数型接口

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

(1) 基本用法
public class FunctionTest {
    public static void main(String[] args) {

        // 执行过程: 给getString方法中传入一个99, 99通过Function的apply转换方法变成了String类型
        String string = getString(99, i -> i.toString());
        // 输出结果: 9911
        System.out.println(string + 11);
    }

    // 传入一个Integer, 获得一个String
    public static String getString(Integer integer, Function<Integer, String> function) {
        String apply = function.apply(integer);
        return apply;
    }
}
(2) 实际运用案例: Optional中通过map()方法转换元素类型

jdk1.8提供的专为空指针判断而诞生的一个工具类Optional中, 存在一个map()方法, 用于转换Optional中的元素类型, map方法中的其中一个参数就是Function, 截图如下:
在这里插入图片描述
关于Optional工具类的使用, 可参考其他博客, 此处不进行说明, 使用案例:

public static void main(String[] args) {

    // 根据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;
        }
    });
}
4. Predicate: 断言型接口

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

(1) 基本用法
public class PredicateTest {

    public static void main(String[] args) {

        // 如果传入的字符串是西瓜, 就返回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);
    }
}
(2) 实际运用案例: 过滤Collection集合中的元素

集合Collection接口中存在一个removeIf()方法, 方法作用就是将集合中的元素按照指定的判断规则移除(也就是过滤), 参数就是Predicate接口, 截图如下:
在这里插入图片描述
假设在某种场景下, 我们要去除关于"张三"或者以"张三"开头的所有人的信息, 代码如下:

public static void main(String[] args) {

    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表达式, 可以让我们在实际开发中的代码更加简洁优雅.

而常见的函数式接口几乎就是本文中提到的四种, 尽管java.util.function包下提供了许许多多的函数式接口, 但是万变不离其宗, 熟练掌握了以上四种, 其余的也是信手拈来.

其他

原创作者: 笔记狂魔, 如有错误, 请多指正

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值