当前位置: 首页 > news >正文

java 函数接口Consumer简介与示例【函数式编程】【Stream】

Java 8 中的 消费者接口Consumer 是一个函数接口,它可以接受一个泛型 类型参数,它属于java.util.function包。我们来看看Java函数接口库中的定义:

@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t); //抽象方法/***下面是一个默认方法***/default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}

accept(T) 方法:是 Consumer 函数式接口的惟一抽象方法,传入单个输入参数,无返回值,可以用于 Lambda 表达式和方法引用。
andThen(Consumer) 方法:是默认方法。上面是这个默认方法的源代码。它可以传入一个 Consumer ,返回组合了两个 Consumer 后的 Consumer ,传入的 Consumer 不能为 null,否则会得到 NullPointerException 。

你可以使用 Consumer 来执行某个动作,比如打印操作、集合中的删除操作等等,该动作接受一个参数并且不返回任何值。

我们先来看两个例程。
第一个例程演示了Consumer 函数式接口两个方法的调用:

import java.util.function.Consumer;
public class ConsumerTest {public static void test() {Consumer<String> first = x -> System.out.println("1."+x.toLowerCase());Consumer<String> second = y -> System.out.println("2." + y);System.out.println("开始");Consumer<String> consume = first.andThen(second);//调用了accept 时,会先执行 first 的代码,再执行 second 的代码consume.accept("World");}public static void main(String[] args) {test();}
}

consume调用accept 方法时,会先执行 first 里的代码,再执行 second 里的代码
测试结果:
在这里插入图片描述

第二个例程:演示集合中的forEach()方法使用Consumer 函数式接口,这是Consumer 函数式接口最常见的用法。

package function;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerTest {//Consumer接口的匿名实现类版本public static void test1() {List<String> list = Arrays.asList("我","爱","北京天安门");list.forEach( new Consumer<String>() {@Overridepublic void accept(String string) {System.out.println(string);}}  );}public static void main(String[] args) {test1();}
}

这段程序很简单,首先初始化一个String类型的集合List,然后向控制台输出集合中每个元素。其中我们注意到forEach方法,它就是Java8中新增加的默认方法。

集合框架中的集合类都实现了迭代接口,forEach方法是Iterable接口的默认方法,被关键字default修饰。这样任何实现该接口的集合都继承forEach方法。Iterable接口的源码如下:

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

如上所示,forEach的实现,首先接收了一个Consumer类型的参数action,进行非空判断,然后遍历当前所有元素,并由action的accept方法进行处理。
forEach方法的输入参数就是一个Consumer类型的参数action。本例中方法test1()是Java7以前版本的老式写法,传入的是一个Consumer接口的匿名实现类:

new Consumer<String>() {@Overridepublic void accept(String string) {System.out.println(string);}
}

在Java8以后版本,第二种方式,函数式接口实例可以使用Lambda表达式,这个Consumer接口的匿名实现类可简写成:
string->System.out.println(string) 此Lambda表达式接收一个参数,没有返回值。
因此,方法test1()就可以改写为如下的Lambda表达式版本:

	//Lambda表达式版本public static void test1() {List<String> list = Arrays.asList("我","爱","北京天安门");list.forEach( string->System.out.println(string) );}

Lambda表达式:string->System.out.println(string)
函数式接口实例第三种方式:方法引用。方法引用的语法是 对象::方法名。还可改写为方法引用 : System.out::println
这二种写法是等效。
所以,我们还可以改写为如下的方法引用的版本:

	//方法引用的版本public static void test1() {List<String> list = Arrays.asList("我","爱","北京天安门");list.forEach( System.out::println );}

改写后版本的效果与前面的Consumer接口的匿名实现类版本完全等价。

再来看一个例子test3(),这个例子中用到了二个Consumer函数,其中consumer是Consumer接口的实现类,还有一个直接使用了Lambda的方法调用:System.out::println

	public static void test3() {List<String> list = new ArrayList<>();Consumer <String> consumer  = x -> {if (x.startsWith("w")){list.add(x);}};Stream.of( "boy","word","cat","way" ).forEach(consumer);list.forEach(System.out::println);}

我们再来看一个复杂一点的例子,例程中用到了集合数组列表,还有一个自定义的类Person。test4()的源代码如下:

	public static void test4() {List<Person> list = new ArrayList<>();list.add(new Person("张振华", 30));list.add(new Person("王五", 28));list.add(new Person("李四", 26));list.add(new Person("李世民", 26));Consumer <Person> consumer  = p -> {if (p.getName().startsWith("张")){list.add(p);}};consumer = consumer.andThen(x -> list.removeIf(y -> y.getName().startsWith("李")));list.forEach(System.out::println);System.out.println("***************************");Stream.of(new Person("王明",32),new Person("张三",18)).forEach(consumer);list.forEach(System.out::println);}

下面是完整的测试例程,有兴趣的读者可以进行编译调试:

package function;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class ConsumerTest {public static void test1() {List<String> list = Arrays.asList("我","爱","北京天安门");//list.forEach(System.out::println);list.forEach(string->System.out.println(string));/*list.forEach( new Consumer<String>() {@Overridepublic void accept(String string) {System.out.println(string);}}  );*/}public static void test3() {List<String> list = new ArrayList<>();Consumer <String> consumer  = x -> {if (x.startsWith("w")){list.add(x);}};Stream.of( "boy","word","cat","way" ).forEach(consumer);list.forEach(System.out::println);}public static void test() {// 创建一个Consumer实例,打印输入的字符串Consumer<String> printer = System.out::println;// 使用accept方法来消费(使用)一个值printer.accept("没有共产党就没有新中国!");// 另一个使用Consumer的例子,这次是更新对象的某个字段的值Consumer<Person> increaseAge = person -> person.setAge(person.getAge() + 1);// 创建一个Person对象Person person = new Person("张振华", 30);// 使用Consumer来增加年龄increaseAge.accept(person);// 打印出增加年龄后的结果System.out.println(person.getName() + ": " + person.getAge());}public static void test4() {List<Person> list = new ArrayList<>();list.add(new Person("张振华", 30));list.add(new Person("王五", 28));list.add(new Person("李四", 26));list.add(new Person("李世民", 26));Consumer <Person> consumer  = p -> {if (p.getName().startsWith("张")){list.add(p);}};consumer = consumer.andThen(x -> list.removeIf(y -> y.getName().startsWith("李")));list.forEach(System.out::println);System.out.println("***************************");Stream.of(new Person("王明",32),new Person("张三",18)).forEach(consumer);list.forEach(System.out::println);}public static void main(String[] args) {test1();test3();test();test4();}
}class Person {private String name;private int age = 32;public Person(String name) { // 构造器this.name = name;}// 构造器public Person(String name, int age) {this.name = name;this.age = age;}// getter和setter方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "姓名:"+name + "  年龄:"+age;}
}

程序测试结果:
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 大学生实用工具!分享5款靠谱AI一键生成毕业论文的网站
  • 5个免费在线 AI 绘画网站推荐,附100+提示词!
  • 什么是上网行为管理呢?【上网行为管理系统功能介绍 】
  • 【C++ 面试 - 面向对象】每日 3 题(六)
  • LeetCode17 电话号码的字母组合
  • STM32——I2C通信外设
  • 深度理解指针(2)
  • 探索Unity3D URP后处理在UI控件Image上的应用
  • 使用cbsd指令快速创建bhyve Ubuntu虚拟机实践
  • Apache CloudStack Official Document 翻译节选(八)
  • IO进程线程8月22日
  • EasyExcel动态实现表头以及数据封装
  • 树莓派开发笔记10-树莓派的HTTP通信实验
  • 移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——6.vector
  • 【YOLO5 项目实战】(6)YOLO5+StrongSORT 目标追踪
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【译】理解JavaScript:new 关键字
  • C++入门教程(10):for 语句
  • Create React App 使用
  • Java多态
  • JWT究竟是什么呢?
  • k8s如何管理Pod
  • MaxCompute访问TableStore(OTS) 数据
  • Python打包系统简单入门
  • vue 配置sass、scss全局变量
  • yii2权限控制rbac之rule详细讲解
  • 前言-如何学习区块链
  • 手写一个CommonJS打包工具(一)
  • 异步
  • 云大使推广中的常见热门问题
  • 【云吞铺子】性能抖动剖析(二)
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • 整理一些计算机基础知识!
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • # .NET Framework中使用命名管道进行进程间通信
  • ## 基础知识
  • (+4)2.2UML建模图
  • (1) caustics\
  • (2)(2.10) LTM telemetry
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (九)One-Wire总线-DS18B20
  • (算法设计与分析)第一章算法概述-习题
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .net MySql
  • .NET 服务 ServiceController
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 使用配置文件
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .php文件都打不开,打不开php文件怎么办
  • ??如何把JavaScript脚本中的参数传到java代码段中