Consumer trong Java 8

1. Giới thiệu Consumer<T>

Trong Java 8, Consumer<T> là một functional interface và do đó nó có thể được sử dụng với lambda expression hoặc method reference cho một mục đích cụ thể nào đó. Consumer<T> chấp nhận một tham số đầu vào, và method này không trả về gì cả.

Mục tiêu chính của Interface Consumer là thực hiện một thao tác trên đối số đã cho.

Interface Consumer được khai báo trong package java.util.function như sau:

Trong đó:

  • void accept(T t) : là một phương thức trừu tượng có thể được sử dụng với lambda expression hoặc method reference cho một mục đích cụ thể nào đó.
  • Phương thức accept() thực hiện một hành động cụ thể trên đối số đã cho.

Interface Consumer còn cung cấp một phương thức mặc định (default method) :

  • default Consumer<T> andThen(Consumer<? super T> after) : phương thức này trả về một Consumer thực hiện hai hành động theo thứ tự, trước tiên là hành động của Consumer mà phương thức được gọi và theo sau bởi hành động của Consumer được truyền vào đối số.

2. Một số ví dụ

2.1. Tạo consumer

package com.maixuanviet.consumer;
 
import java.util.function.Consumer;
 
public class ConsumerExample1 {
 
    static void printValue(int val) {
        System.out.println(val);
    }
 
    public static void main(String[] args) {
        // Create Consumer interface
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String name) {
                System.out.println("Hello, " + name);
            }
        };
        // Calling Consumer method
        consumer.accept("maixuanviet"); // Hello, maixuanviet
 
        // Create Consumer interface with lambda expression
        Consumer<String> consumer1 = (name) -> System.out.println("Hello, " + name);
        // Calling Consumer method
        consumer1.accept("maixuanviet"); // Hello, maixuanviet
 
        // Create Consumer interface with method reference
        Consumer<Integer> consumer2 = ConsumerExample1::printValue;
        // Calling Consumer method
        consumer2.accept(12); // 12
    }
}

Trong ví dụ trên, tôi đã tạo 2 consumer:

  • consumer1 : được tạo bằng cách sử dụng lambda expression.
  • consumer2 : được tạo bằng cách sử dụng method reference.

Để thực thi consumer, chúng ta gọi phương thức mặc định accept() được cung cấp trong Interface Consumer. Phương thức này sẽ thực thi đoạn code được cài đặt thông qua lambda expression hoặc method reference.

2.2. Sử dụng phương thức mặc định andThen()

package com.maixuanviet.consumer;
 
import java.util.function.Consumer;
 
public class ConsumerAndThenExample {
 
    public static final int TEST_NUMBER = 5;
 
    public static void main(String[] args) {
        Consumer<Integer> times2 = (e) -> System.out.println(e * 2);
        Consumer<Integer> squared = (e) -> System.out.println(e * e);
        Consumer<Integer> isOdd = (e) -> System.out.println(e % 2 == 1);
 
        // perform every consumer
        times2.accept(TEST_NUMBER); // 10
        squared.accept(TEST_NUMBER); // 25
        isOdd.accept(TEST_NUMBER); // true
 
        // perform 3 methods in sequence
        Consumer<Integer> combineConsumer = times2.andThen(squared).andThen(isOdd);
        combineConsumer.accept(TEST_NUMBER); // 10 25 true
    }
}

Trong phương thức trên, tôi đã tạo 3 consumer. Thay vì gọi phương thức accept() lần lượt cho từng consumer, tôi đã kết hợp chúng thông qua phương thức mặc định andThen(), để thực thi tất cả các consumer đó, chúng ta chỉ việc gọi một combineConsumer duy nhất.

2.3. Sử dụng Consumer với forEach loop

Như chúng ta đã biết, phương thức foreach là một phương thức mặc định(default method) được định nghĩa trong interface Iterable và Stream. Các lớp Collection extends từ interface Iterable có thể sử dụng vòng lặp forEach() để duyệt các phần tử. Phương thức này chấp nhận một đối số đầu vào là Consumer. Do đó chúng ta có thể viết theo một trong các cách sau:

  • Tạo một consumer và truyền như đối số đầu vào.
  • Sử dụng trực tiếp Method referenece như đối số đầu vào.
  • Sử dụng trực tiếp Lambda expression như đối số đầu vào.
package com.maixuanviet.consumer;
 
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
 
class Programing {
    public String language;
    public int experience;
 
    public Programing(String language, int experience) {
        this.language = language;
        this.experience = experience;
    }
 
    public void print() {
        System.out.println("Language: " + language + " - Experience: " + experience);
    }
}
 
public class ConsumerExample2 {
 
    public static void main(String[] args) {
 
        List<Programing> list = new ArrayList<>();
        list.add(new Programing("Java", 5));
        list.add(new Programing("PHP", 2));
        list.add(new Programing("C#", 1));
 
        // Creating instance of Consumer functional interface
        Consumer<Programing> consumer = (p) -> System.out.println(
                "Name: " + p.language + " - Experience: " + p.experience);
 
        System.out.println("Using Consumer: ");
        list.forEach(consumer);
 
        System.out.println("\nUsing Method Reference: ");
        list.forEach(Programing::print);
 
        System.out.println("\nUsing Lambda Expression: ");
        list.forEach(s -> s.print());
    }
}

Output của chương trình trên:

Using Consumer: 
Name: Java - Experience: 5
Name: PHP - Experience: 2
Name: C# - Experience: 1
 
Using Method Reference: 
Language: Java - Experience: 5
Language: PHP - Experience: 2
Language: C# - Experience: 1
 
Using Lambda Expression: 
Language: Java - Experience: 5
Language: PHP - Experience: 2
Language: C# - Experience: 1

2.4. Sử dụng Consumer với các lớp cho kiểu dữ liệu nguyên thủy (primitive type)

Java 8 cung cấp một số Interface Consumer cho các wrapper class của kiểu dữ liệu nguyên thủy như sau:

  • IntConsumer : chấp nhận một giá trị kiểu Int duy nhất.
  • LongConsumer : chấp nhận một giá trị kiểu Long duy nhất.
  • DoubleConsumer : chấp nhận một giá trị kiểu Double duy nhất.
package com.maixuanviet.consumer;
 
import java.util.Arrays;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
 
public class ConsumerExample3 {
 
    public static void main(String[] args) {
 
        System.out.print("IntConsumer: ");
        int[] intNumbers = { 3, 5, 6, 2, 1 };
        IntConsumer intConsumer = i -> System.out.print(i + " ");
        Arrays.stream(intNumbers).forEach(intConsumer);
 
        System.out.print("\nLongConsumer: ");
        long[] longNumbers = { 3, 5, 6, 2, 1 };
        LongConsumer longConsumer = l -> System.out.print(l + " ");
        Arrays.stream(longNumbers).forEach(longConsumer);
 
        System.out.print("\nDoubleConsumer: ");
        double[] dbNumbers = { 3.2, 5.1, 6.3, 2.5, 1.0 };
        DoubleConsumer dbConsumer = d -> System.out.print(d + " ");
        Arrays.stream(dbNumbers).forEach(dbConsumer);
    }
}

Output của chương trình trên:

IntConsumer: 3 5 6 2 1
LongConsumer: 3 5 6 2 1
DoubleConsumer: 3.2 5.1 6.3 2.5 1.0

2.5. Sử dụng Consumer 2 đối số với BiConsumer

Như đã nói ở phần trên Consumer chỉ chấp nhận 1 đối số đầu vào, để có thể sử dụng Consumer với 2 đối số đầu vào chúng ta sử dụng Interface BiConsumer.

Về cơ bản, interface BiConsumer không khác biệt so với Consumer, ngoại trừ nó chấp nhận 2 đối số đầu vào.

Ví dụ:

package com.maixuanviet.consumer;
 
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
 
public class BiConsumerExample {
 
    public static void main(String[] args) {
 
        Map<String, Integer> map = new HashMap<>();
        map.put("Java", 5);
        map.put("PHP", 2);
        map.put("C#", 1);
         
        BiConsumer<String, Integer> biConsumer = 
                (key, value) -> System.out.println("Key: " + key + " - Value: " + value);
        map.forEach(biConsumer);
    }
}

Output của chương trình trên:

Key: C# - Value: 1
Key: Java - Value: 5
Key: PHP - Value: 2

Related posts: