1. 程式人生 > 其它 >jdk8函式式介面——Consumer介紹

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