jdk8函數式接口——Consumer介紹


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM