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类型

 

 

 

原文链接:

https://blog.csdn.net/rz_0212/article/details/89575600

https://www.cnblogs.com/coderxx/p/11182892.html

posted on 2022-03-16 16:18  yuluoxingkong  阅读(1402)  评论(0编辑  收藏  举报