Java 8 Consumer、Supplier、Predicate、Function

这几个接口都在 java.util.function 包下的,分别是Consumer(消费型)、supplier(供给型)、predicate(谓词型)、function(功能性)
下面从具体的应用场景来讲讲这个接口的用法。

Consumer接口

源码:

Consumer.java

/**
 * 代表这样一种操作: 接收一个单值输入,没有返回值。与大部分函数式接口不同,
 *  Consumer一般通过"副作用"来操作。
 * Consumer 的函数式方法是accept
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * 这个默认方法与Function接口中的andThen方法类似,对于给定的入参after(类型也是Consumer),
     * 先执行上面的accept方法,再执行after中的accept方法。
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

使用Consumer接口的示例

     @Test
    public void test_Consumer(){
        //1. 使用consumer接口实现方法
        System.out.println("使用consumer接口实现方法");
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        Stream<String> stream = Stream.of("aaa", "bbb");
        stream.forEach(consumer);

        System.out.println("*********************");

        //2. 使用lambda表达式, Stream的forEach方法需要的入参就是一个consumer接口
        System.out.println("使用lambda表达式");
        Consumer<String> consumer1 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
        stream = Stream.of("aaa", "bbb");
        stream.forEach(consumer1);
        //更直接的方式
        //stream.forEach((s) -> System.out.println(s));
        System.out.println("********************");

        //3. 使用方法引用,方法引用也是一个consumer
        System.out.println("使用方法引用");
        Consumer consumer2 = System.out::println;
        stream = Stream.of("aaa", "bbb");
        stream.forEach(consumer2);
        //更直接的方式
        //stream.forEach(System.out::println);
        System.out.println("********************");

        //4. 演示Consumer的andThen方法
        Consumer<String> addHello = s -> System.out.println("hello");
        Consumer consumer3 = s -> System.out.println(s);
        //先执行consumer3定义的accept方法,再执行addHello定义的accept
        System.out.println("演示Consumer的andThen方法");
        stream = Stream.of("aaa", "bbb");
        stream.forEach(consumer3.andThen(addHello));
    }

输出:

使用consumer接口实现方法
aaa
bbb
*********************
使用lambda表达式
aaa
bbb
********************
使用方法引用
aaa
bbb
********************
演示Consumer的andThen方法
aaa
hello
bbb
hello

说明:

  1. Consumer是一个接口,并且只要实现一个 accept 方法,就可以作为一个“消费者”输出信息。
  2. lambda 表达式、方法引用的返回值都是 Consumer 类型,所以,他们能够作为StreamforEach 方法的参数,并且输出一个值。

其实除了StreamforEach方法, 我们常见的List列表的forEach方法也可以接收一个Consumer来遍历列表中的每个元素并对其进行Consumeraccept方法中定义的操作:
java.lang.IterableforEach方法源码:

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

forEach的参数是一个Consumer,从forEach这个方法的定义来看,现在我们可以理解为“遍历我所包含的所有元素,对每个元素都执行一次action.accept()
所以客户端可以这样使用ListforEach:

List<String> list = new ArrayList<>();
list.add("Hello ");
list.add("World!");
list.forEach(s -> System.out.println(s)); // 这里当然也可以用更为简洁的方法引用来改写

## Supplier 接口 #### 源码 > Supplier.java ``` @FunctionalInterface public interface Supplier {

/**
* Gets a result.
*
* @return a result
*/
T get();
}

`Supplier`接口是一个供给型的接口,其实,说白了就是一个容器,可以用来存储数据,然后可以供其他方法使用的这么一个接口。至于如何获取数据,交给用户去实现`Supplier`的`get`方法。
#### 使用Supplier接口的示例

@Test
public void test_Supplier() {
//① 使用Supplier接口实现方法,只有一个get方法,无参数,返回一个值
Supplier supplier = new Supplier() {
@Override
public Integer get() {
//返回一个随机值
return new Random().nextInt();
}
};
System.out.println("获取随机int");
System.out.println(supplier.get());

System.out.println("********************");

//② 使用lambda表达式,
supplier = () -> new Random().nextInt();
System.out.println("使用lambda表达式定义supplier");
System.out.println(supplier.get());
System.out.println("********************");

//③ 使用方法引用
System.out.println("使用方法引用");
Supplier supplier2 = Math::random;
System.out.println(supplier2.get());

Integer[] integers = {1, 2, 3, 4, 5};
List list = Arrays.asList(integers);
Stream stream = list.stream();
//返回一个optional对象, optional对象可以持有元素或者null
Optional first = stream.filter(i -> i > 4)
.findFirst();
//optional对象有需要Supplier接口的方法
//orElse,如果first中存在数,就返回这个数,如果不存在,就返回传入orElse中的数字
System.out.println("使用optional对象找到大于4的第一个元素并返回,找不到返回-100");
System.out.println(first.orElse(-100));
System.out.println("********************");

//这次找大于100的数字
stream = list.stream();
Optional above100 = stream.filter(integer -> integer > 100).findFirst();
Supplier supplier3 = () -> new Random().nextInt();

//orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值
System.out.println("orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值");
System.out.println(above100.orElseGet(supplier3));
}

输出:

获取随机int
-1992653641


使用lambda表达式定义supplier
-1661833099


使用方法引用
0.4916754644996516
使用optional对象找到大于4的第一个元素并返回,找不到返回-100
5


orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier3返回的值
-454437243

说明:
1. `Supplier` 接口可以理解为一个容器,用于装数据的。
2. `Supplier` 接口有一个 `get` 方法,用户重写这个方法以提供返回。
<br/>

#### Supplier接口示例补充:
> CallableLock.java

public class CallableLock {
public V lock(Supplier supplier){
try {
doLock();
return supplier.get();
}finally {
unLock();
}
}

private boolean doLock(){
System.out.println("Lock!");
return true;
}

private void unLock(){
System.out.println("Unlock!");
}
}

> Processor.java

public class Processor {
public boolean process() {
System.out.println("Processing");
try {
Thread.sleep(10 * 1000);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
}

> LockTest.java

public class LockTest {

@Test
public void lockTest(){
CallableLock callableLock = new CallableLock<>();
Processor processor = new Processor();
callableLock.lock(processor::process);
System.out.println("");
callableLock.lock(()->processor.process());
System.out.println("
");
callableLock.lock(new Supplier() {
@Override
public Boolean get() {
return processor.process();
}
});
System.out.println("====================");
}
}

<br/>

## Predicate 接口
#### 源码
> Predicate.java

@FunctionalInterface
public interface Predicate {

/**
* 计算给定的boolean表达式的真假
*
* @param t 入参
* @return {@code true} 根据输入t是否符合test函数中定义的判断逻辑返回true或false,
*/
boolean test(T t);

//下面在接口中默认实现的and方法、negate方法、or方法、isEqual方法, 可以实现嵌套判断
//and方法提供逻辑与功能,具有短路效应
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

//negate方法提供逻辑非功能
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*/
default Predicate negate() {
return (t) -> !test(t);
}

// or方法提供逻辑或功能,具有短路效应
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}

/** isEqual方法提供逻辑相等判断
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*/
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}

说明: 默认实现的`and`、 `negate`、 `or`、`isEqual`方法都返回`Predicate`对象。 用于嵌套的逻辑判断。
#### Predicate接口示例

@Test
public void test_Predicate() {
Integer[] integers = {1, 2, 3, 4, 5};
List list = Arrays.asList(integers);
Stream stream = list.stream();
// 基本测试, 使用predicate打印所有大于3的数字
Predicate predicate = integer -> integer > 3;
System.out.println("使用predicate打印所有大于3的数字");
stream.filter(predicate).forEach((integer)-> System.out.println(integer));
System.out.println("********************");

//默认方法测试,这里只测试and方法, 其他的方法类似
System.out.println("测试and方法, 使用两个predicate之间的and逻辑,打印所有大于3且为偶数的数字");
Predicate even = integer -> integer%2 == 0;
stream = list.stream();
stream.filter(predicate.and(even)).forEach(System.out::println);
}

测试结果:

使用predicate打印所有大于3的数字
4
5


测试and方法, 使用两个predicate之间的and逻辑,打印所有大于3且为偶数的数字

说明:
1. `Predicate` 是一个谓词型接口,其实只是起到一个判断作用。
2. `Predicate` 通过实现一个 `test` 方法做判断。
3. `Predicate`有默认实现的逻辑判断方法
<br/>

## Function接口
#### 基本使用
见 [Java8 Function接口(apply compose andThen)](https://www.cnblogs.com/greatLong/articles/11975684.html)
<br/>
#### 链式调用进阶使用
首先定义接口`ProcessUnit`继承`Function`接口并提供`apply`方法的默认实现:
> ProcessUnit.java

public interface ProcessUnit<T, R> extends Function<T, R> {
@Override
default R apply(T t){
return process(t);
}

R process(T t);
}

这里对`apply`方法的默认实现是调用`ProcessUnit`定义的`process`方法,这个方法交给实现`ProcessUnit`的类去实现,从而可以在这里写自己的处理逻辑。在使用的时候可以使用`ProcessUnit`从`Function`接口中继承来的`compose`和`andThen`方法对逻辑处理进行组合。
下面的`AndThen1`、`AndThen2`和`AndThen3`都分别实现了`ProcessUnit`接口,并重写了`process`方法来提供自己的处理逻辑。
> AndThen1.java

public class AndThen1 implements ProcessUnit<Integer, String>{
@Override
public String process(Integer integer) {
integer += 1;
System.out.println("I am in andThen1, the number is " + integer);
String s = String.valueOf(integer);
return s;
}
}

> AndThen2.java

public class AndThen2 implements ProcessUnit<String, String>{
@Override
public String process(String string) {
Integer integer = Integer.parseInt(string) + 1;
System.out.println("I am in andThen2, the number is " + integer);
return String.valueOf(integer);
}
}

> AndThen3.java

public class AndThen3 implements ProcessUnit<String, String>{
@Override
public String process(String string) {
Integer integer = Integer.parseInt(string) + 1;
System.out.println("I am in andThen3, the number is " + integer);
return String.valueOf(integer);
}
}

测试方法:
> test.java

public class test {

Function<Integer, String> function;

@Before
public void prepare(){
System.out.println("Prepare the test ... ");
System.out.println("++++++++++++++++++++++++++++++");
function = new AndThen1().andThen(new AndThen2()).andThen(new AndThen3());
}

@Test
public void testAndThen(){
System.out.println(function.apply(100));
}

@After
public void finish(){
System.out.println("==============================");
System.out.println("finish test");
}
}

在测试类中定义了一个`Function`对象来接收方法调用链实例。
测试结果:

Prepare the test ...
++++++++++++++++++++++++++++++
I am in andThen1, the number is 101
I am in andThen2, the number is 102
I am in andThen3, the number is 103
103

finish test

Process finished with exit code 0


> 参考文章
>> [Java8之Consumer、Supplier、Predicate和Function攻略](https://www.cnblogs.com/SIHAIloveYAN/p/11288064.html)
posted @ 2019-12-03 15:03  lllunaticer  阅读(1706)  评论(0)    收藏  举报