Consumer介绍与实例分析
函数式接口:@FunctionalInterface
Consumer(消费者)
函数式接口:@FunctionalInterface
自从jdk8提供了函数式接口这一新的特性,极大地精简了java开发的方式。而在jdk8之前的版本,函数仅仅只能传递参数,而不能将一个函数或者说是行为传递过去,这意味着我们在调用某个函数时,该函数所表示的执行功能已经很明确了,对于lambda表达式来说,函数的调用,是将函数的行为传递过去,真正执行的是调用时传递的行为。@FunctionalInterface注解是标识一个接口是函数式接口。那么什么样的接口是函数式接口呢?
下面是@FunctionalInterface的注释说明:
Conceptually, a functional interface has exactly one abstract method. Since {@linkplain java.lang.reflect.Method#isDefault() default methods} have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of {@code java.lang.Object}, that also does <em>not</em> count toward the interface's abstract method count since any implementation of the interface will have an implementation from {@code java.lang.Object} or elsewhere. Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
该注释说了,一个函数式接口应该只有一个抽象方法,对于default methods,是有一个实现,所以它们不是抽象的,这里就说明了jdk8的接口支持方法的实现。如果一个接口声明了一个抽象方法,该方法是被Object类给重写的,那么它不会为该接口的抽象方法增加,因为在Object或者别处会有一个具体的实现。函数式接口的实例可以通过lambda表达式,方法引用,构造方法引用的方式创建出来。这里我们就理解了函数式接口和lambda表达式之间的关系了。下面我主要讲解一个函数式接口Consumer的用法。
Consumer(消费者)
对于Consumer这个接口,我们来看一下它提供的抽象方法是什么?
/** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t);
accept(T t),接受一个参数,没有返回值。举一个例子:
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9); // 通过lambda表达式构造出Consumer对象 list.forEach(i -> System.out.println(i));
这里是通过lambda表达式构造出Consumer对象,将list中每一个元素,传递给consumer,执行打印的操作,我再把这个例子做一个变化
// 通过lambda表达式构造出Consumer对象 list.forEach(i -> System.out.println(i * 2));
这里打印的元素是乘2后的结果,这就说明了通过lambda表达式,我们传递的是行为,accept(T t)方法只负责接收一个参数,至于要做什么,是我们再调用的时候,把行为传递过去。
另外还可以使用方法引用的方式来调用Consumer的accept方法。
// 通过方法引用的方式构造出Consumer对象 list.forEach(System.out::println);
比如将给定的一批用户里面的名称为"lisi"的用户都给打包起来
/** * 此处使用lombok插件(值得了解) */ @Data @Accessors(chain = true) @AllArgsConstructor public class Person { private Integer age; private String name; } List<Person> lisiList = new ArrayList<>(); Consumer<Person> consumer = x -> { if (x.getName().equals("lisi")){ lisiList.add(x); } }; Stream.of( new Person(21,"zhangsan"), new Person(22,"lisi"), new Person(23,"wangwu"), new Person(24,"wangwu"), new Person(23,"lisi"), new Person(26,"lisi"), new Person(26,"zhangsan") ).forEach(consumer); System.out.println(JSON.toJSONString(lisiList));
结果为:
[{"age":22,"name":"lisi"},{"age":23,"name":"lisi"},{"age":26,"name":"lisi"}]
这里也可以实现遍历每一个元素并打印出来,这是通过方法引用的方式来构造出的Consumer对象。"::"这里两个连续的冒号,是jdk8支持的语法,可以自动定位到具体的函数式接口,这里就可以自动定位到Consumer。
Consumer中还提供了一个默认方法,andThen,来看一下
/** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; }
该方法默认实现,它接收一个Consumer对象,同时会返回一个Consumer对象,返回的Consumer对象还可以继续调用andThen方法,这样该方法就实现了将执行操作给串行化。举个例子:
public static void main(String[] args) { ConsumerTest02 test = new ConsumerTest02(); List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9); test.print(list, item -> System.out.print(" consumer1:" + item * 2), item -> System.out.print(" consumer2:" + item * 3)); } /* andThen方法, 将参数传递给调用者执行accept方法,然后再传给第二个consumer执行accept方法。 */ public void print(List<Integer> list, IntConsumer con1, IntConsumer con2) { list.forEach(item -> con1.andThen(con2).accept(item)); }
该示例构造了两个Consumer对象,通过consumer的andThen方法,将两个操作给串行起来,对于list中每个元素,都会先执行con1的appect方法,再执行con2的accept方法。
打印结果:
consumer1:2 consumer2:3 consumer1:4 consumer2:6 consumer1:6 consumer2:9 consumer1:8 consumer2:12 consumer1:10 consumer2:15 consumer1:12 consumer2:18 consumer1:14 consumer2:21 consumer1:16 consumer2:24 consumer1:18 consumer2:27
1
比如将给定的一批用户里面的名称为"lisi"且年龄大于22岁的用户都给打包起来
List<Person> lisiList = new ArrayList<>(); Consumer<Person> consumer = x -> { if (x.getName().equals("lisi")){ lisiList.add(x); } }; consumer = consumer.andThen( x -> lisiList.removeIf(y -> y.getAge() < 23) ); Stream.of( new Person(21,"zhangsan"), new Person(22,"lisi"), new Person(23,"wangwu"), new Person(24,"wangwu"), new Person(23,"lisi"), new Person(26,"lisi"), new Person(26,"zhangsan") ).forEach(consumer); System.out.println(JSON.toJSONString(lisiList)); }
结果为:
[{"age":23,"name":"lisi"},{"age":26,"name":"lisi"}]
与Consumer相关的接口
- BiConsumer<T, U>
处理一个两个参数
- DoubleConsumer
处理一个double类型的参数
- IntConsumer
处理一个int类型的参数
- LongConsumer
处理一个long类型的参数
- ObjIntConsumer
处理两个参数,且第二个参数必须为int类型
- ObjLongConsumer
处理两个参数,且第二个参数必须为long类型
原文链接: