Giải thích về Nhà cung cấp & Người tiêu dùng Java 8 cho người học


101

Là một lập trình viên không phải lập trình viên Java đang học Java, tôi đang đọc về SupplierConsumergiao diện vào lúc này. Và tôi không thể hiểu hết cách sử dụng và ý nghĩa của chúng. Khi nào và tại sao bạn sẽ sử dụng các giao diện này? Ai đó có thể cho tôi một ví dụ đơn giản về vấn đề này không ... Tôi thấy các ví dụ về Tài liệu không đủ ngắn gọn để tôi hiểu.


4
Mỗi Trang của Tài liệu API có một liên kết có nhãn “SỬ DỤNG” ở trên cùng mà bạn có thể nhấp vào ConsumerSupplierBạn cũng có thể tìm kiếm hướng dẫn cho Consumer
Holger

7
Tôi thích câu trả lời của Stuart Marks. Và tôi nghĩ rằng hầu hết những người trả lời dưới đây đều bỏ sót ý. Câu hỏi không phải là "làm thế nào" để viết Nhà cung cấp, Người tiêu dùng và Chức năng. Đó là "tại sao" trên thế giới mà bạn muốn? Đối với một người không quen với chúng, chúng làm cho mã phức tạp hơn nhiều. Nhưng lợi ích của việc sử dụng chúng là không rõ ràng.
anton1980, 16/02/19

Theo như tôi thấy (và tôi chia sẻ sự thất vọng của bạn với các mô tả tiếp tuyến) thì đó chỉ là một cách khéo léo để trừu tượng hóa cả kiểu đối tượng và xử lý đối tượng từ một đối tượng được sử dụng trong một đoạn mã. Điều này cho phép áp dụng cùng một mã này cho nhiều loại đối tượng khác nhau bằng cách chỉ cần xác định các lớp mới khác nhau và đưa chúng vào giao diện Nhà cung cấp và Người tiêu dùng. Vì vậy, trong hệ thống hồ sơ cảnh sát, cùng một mã bề ngoài được sử dụng cho tất cả các nghi phạm nhưng bản in cuối cùng cho mỗi nghi phạm phụ thuộc vào phân loại của từng nghi phạm, ví dụ: "công dân", "nhỏ mọn", "ăn trộm", "trọng tội", "cứng rắn", vv
Trunk

Câu trả lời:


95

Đây là Nhà cung cấp:

public Integer getInteger() {
    return new Random().nextInt();
}

Đây là Người tiêu dùng:

public void sum(Integer a, Integer b) {
    System.out.println(a + b);
}

Vì vậy, theo thuật ngữ của giáo dân, nhà cung cấp là một phương thức trả về một số giá trị (như giá trị trả về của nó). Trong khi đó, người dùng là một phương thức sử dụng một số giá trị (như trong đối số phương thức) và thực hiện một số thao tác trên chúng.

Chúng sẽ chuyển đổi thành một cái gì đó như sau:

// new operator itself is a supplier, of the reference to the newly created object
Supplier<List<String>> listSupplier = ArrayList::new;
Consumer<String> printConsumer = a1 -> System.out.println(a1);
BiConsumer<Integer, Integer> sumConsumer = (a1, a2) -> System.out.println(a1 + a2);

Về cách sử dụng, ví dụ rất cơ bản sẽ là: Stream#forEach(Consumer)method. Nó có một Người tiêu dùng, sử dụng phần tử từ luồng bạn đang lặp lại và thực hiện một số hành động trên mỗi phần tử đó. Có lẽ là in chúng.

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);

3
Vì vậy, Nhà cung cấp là một cách để tạo một phiên bản của một phương thức trả về 'cái gì đó'?
james emanon

3
@jamesemanon Chính xác. Đó có thể là một tham chiếu phương thức hoặc lambda.
Rohit Jain

14
Lợi ích của việc này thay vì gọi phương thức trực tiếp là gì? Có phải vì Nhà cung cấp có thể hoạt động như một người trung gian và trao đi giá trị “trả lại” đó?
james emanon

1
Người tiêu dùng <Số nguyên, Số nguyên> không hợp lệ. Người tiêu dùng có một tham số loại duy nhất.
nascar

2
Nhưng TẠI SAO lại tạo ra một cấu trúc như vậy? Vấn đề nào được giải quyết bằng cách sử dụng nó trong Java?
Thân cây

178

Lý do bạn gặp khó khăn trong việc nắm bắt ý nghĩa của các giao diện chức năng chẳng hạn như những giao diện trong java.util.functionđó là các giao diện được định nghĩa ở đây không có bất kỳ ý nghĩa nào! Chúng hiện diện chủ yếu để biểu thị cấu trúc , không phải ngữ nghĩa .

Điều này không điển hình cho hầu hết các API Java. API Java điển hình, chẳng hạn như một lớp hoặc giao diện, có ý nghĩa và bạn có thể phát triển một mô hình tinh thần cho những gì nó đại diện và sử dụng cái đó để hiểu các hoạt động trên đó. Hãy xem xét java.util.Liství dụ. A Listlà vật chứa các đối tượng khác. Chúng có một trình tự và một chỉ mục. Số đối tượng có trong danh sách được trả về bởi size(). Mỗi đối tượng có một chỉ mục trong phạm vi 0..size-1 (bao gồm). Đối tượng tại chỉ mục i có thể được truy xuất bằng cách gọi list.get(i). Và kể từ đó trở đi.

Các giao diện chức năng trong java.util.functionkhông có bất kỳ ý nghĩa nào như vậy. Thay vào đó, chúng là các giao diện chỉ thể hiện cấu trúc của một hàm, chẳng hạn như số lượng đối số, số lượng giá trị trả về và (đôi khi) liệu một đối số hoặc giá trị trả về có phải là một nguyên thủy hay không. Như vậy chúng ta có một cái gì đó giống như Function<T,R>đại diện cho một chức năng mà phải mất một đối số duy nhất của loại T và trả về một giá trị kiểu R . Đó là nó. Chức năng đó làm gì? Chà, nó có thể làm bất cứ điều gì ... miễn là nó nhận một đối số duy nhất và trả về một giá trị duy nhất. Đó là lý do tại sao đặc tả cho Function<T,R>không nhiều hơn "Đại diện cho một hàm chấp nhận một đối số và tạo ra một kết quả."

Rõ ràng, khi chúng ta viết mã, nó có ý nghĩa, và ý nghĩa đó phải đến từ đâu đó. Trong trường hợp của các giao diện chức năng, ý nghĩa xuất phát từ ngữ cảnh mà chúng được sử dụng. Giao diện Function<T,R>không có ý nghĩa riêng biệt. Tuy nhiên, trong java.util.Map<K,V>API, có những điều sau:

V computeIfAbsent(K key, Function<K,V> mappingFunction)

(các ký tự đại diện được giải thích cho ngắn gọn)

Ah, việc sử dụng Functionnày là như một "chức năng ánh xạ". Cái đó làm cái gì? Trong ngữ cảnh này, nếu keychưa có trong bản đồ, hàm ánh xạ được gọi và được trao khóa và được mong đợi tạo ra một giá trị, và cặp khóa-giá trị kết quả được chèn vào bản đồ.

Vì vậy, bạn không thể nhìn vào thông số kỹ thuật cho Function(hoặc bất kỳ giao diện chức năng nào khác, cho vấn đề đó) và cố gắng phân biệt ý nghĩa của chúng. Bạn phải xem chúng được sử dụng ở đâu trong các API khác để hiểu ý nghĩa của chúng và ý nghĩa đó chỉ áp dụng cho ngữ cảnh đó.


3
Vì vậy, về cơ bản, nó chỉ hoạt động như kiểu
JGuo

Có thể là một thông tin hữu ích khác là các giao diện chức năng có thể có nhiều phương pháp được triển khai có thể thêm hành vi vào mã của bạn
Jhon Mario Lotero

28

A Supplierlà bất kỳ phương thức nào không nhận đối số và trả về một giá trị. Công việc của nó theo nghĩa đen là cung cấp một thể hiện của một lớp mong đợi. Ví dụ: mọi tham chiếu đến phương thức 'getter' làSupplier

public Integer getCount(){
    return this.count;
}

Tham chiếu phương thức cá thể của nó myClass::getCountlà một ví dụ của Supplier<Integer>.

A Consumerlà bất kỳ phương thức nào nhận đối số và không trả về gì. Nó được gọi cho các tác dụng phụ của nó. Theo thuật ngữ Java, a Consumerlà một thành ngữ cho một voidphương thức. phương thức 'setter' là một ví dụ điển hình:

public void setCount(int count){
    this.count = count;
}

Tham chiếu phương thức thể hiện của nó myClass::setCountlà một thể hiện của Consumer<Integer>IntConsumer.

A Function<A,B>là bất kỳ phương thức nào nhận một đối số của một kiểu và trả về một kiểu khác. Đây có thể được coi là một 'sự biến đổi'. Nhận Function<A,B>một Avà trả về a B. Đáng chú ý là đối với một giá trị nhất định củaA , hàm phải luôn trả về một giá trị cụ thể là B. ABtrên thực tế có thể là cùng một loại, chẳng hạn như sau:

public Integer addTwo(int i){
    return i+2;
}

Tham chiếu phương thức thể hiện của nó myClass:addTwolà a Function<Integer, Integer>và a ToIntFunction<Integer>.

Một tham chiếu phương thức Class tới một getter là một ví dụ khác về một hàm.

public Integer getCount(){
    return this.count;
}

Tham chiếu phương thức lớp của nó MyClass::getCountlà một thể hiện của Function<MyClass,Integer>ToIntFunction<MyClass>.


15

Tại sao Người tiêu dùng / Nhà cung cấp / các giao diện chức năng khác được định nghĩa trong gói java.util. Chức năng : Người tiêu dùng và Nhà cung cấp là hai trong số rất nhiều giao diện chức năng tích hợp được cung cấp trong Java 8. Mục đích của tất cả các giao diện chức năng tích hợp này là để cung cấp một "khuôn mẫu" sẵn sàng cho các giao diện chức năng có các bộ mô tả chức năng chung (chữ ký / định nghĩa phương pháp chức năng).

Giả sử chúng ta có yêu cầu chuyển đổi kiểu T sang kiểu khác R. Nếu chúng ta truyền bất kỳ hàm nào được định nghĩa như thế này dưới dạng tham số cho một phương thức, thì phương thức đó sẽ cần phải xác định một Giao diện chức năng có phương thức hàm / trừu tượng nhận tham số kiểu T làm đầu vào và cho một tham số kiểu R làm đầu ra. Bây giờ, có thể có nhiều tình huống như thế này và (các) lập trình viên sẽ kết thúc việc xác định nhiều giao diện chức năng cho nhu cầu của họ. Để tránh loại kịch bản này, hãy dễ dàng lập trình & mang lại một tiêu chuẩn chung trong việc sử dụng các giao diện chức năng, một tập hợp các giao diện chức năng tích hợp sẵn như Predicate, Function, Consumer & Supplier đã được xác định.

Người tiêu dùng làm gì : Giao diện chức năng của người tiêu dùng chấp nhận một đầu vào, thực hiện điều gì đó với đầu vào đó và không đưa ra bất kỳ đầu ra nào. Định nghĩa của nó là như thế này (từ Nguồn Java) -

@FunctionalInterface
public interface Consumer<T> {
 void accept(T t);
}

Ở đây accept () là phương thức function \ abstract, phương thức này nhận đầu vào và không trả về đầu ra. Vì vậy, nếu bạn muốn nhập Số nguyên, hãy làm điều gì đó với nó mà không có đầu ra, sau đó thay vì xác định giao diện của riêng bạn, hãy sử dụng một phiên bản của Người tiêu dùng.

Nhà cung cấp làm gì : Giao diện chức năng của nhà cung cấp không nhận bất kỳ đầu vào nào mà trả về đầu ra. Nó được định nghĩa như thế này (từ Nguồn Java) -

@FunctionalInterface
public interface Supplier<T> {
  T get();
}

Bất cứ nơi nào bạn cần một hàm trả về thứ gì đó, hãy nói là Số nguyên, nhưng không có đầu ra, hãy sử dụng một phiên bản của Nhà cung cấp.

Trong trường hợp cần rõ ràng hơn, cùng với việc sử dụng ví dụ, giao diện Người tiêu dùng & Nhà cung cấp, thì bạn có thể tham khảo các bài đăng trên blog của tôi - http://www.javabrahman.com/java-8/java-8-java-util- chức năng-người tiêu dùng-hướng dẫn-với-ví dụ / http://www.javabrahman.com/java-8/java-8-java-util- Chức năng-supplier-tutorial-with-examples/


12

1. Ý nghĩa

Xem câu trả lời của tôi cho câu hỏi của tôi ở đây và cả câu trả lời khác ở đây , nhưng tóm lại, những Giao diện mới này cung cấp tính quy ướcmô tả cho mọi người sử dụng (+ chuỗi phương pháp funky chẳng hạn như.forEach(someMethod().andThen(otherMethod()))

2. Sự khác biệt

Người tiêu dùng : Lấy một cái gì đó, làm một cái gì đó, không trả lại gì:void accept(T t)

Nhà cung cấp: Không mất gì, trả lại thứ gì đó: T get()(ngược lại với Người tiêu dùng, về cơ bản là một phương pháp 'getter' phổ biến)

3. Cách sử dụng

// Consumer: It takes something (a String) and does something (prints it) 
    List<Person> personList = getPersons();

     personList.stream()
                    .map(Person::getName)    
                    .forEach(System.out::println); 

Nhà cung cấp: bọc mã lặp lại, ví dụ thời gian thực thi mã

public class SupplierExample {

    public static void main(String[] args) {

        // Imagine a class Calculate with some methods
        Double result1 = timeMe(Calculate::doHeavyComputation);
        Double result2 = timeMe(Calculate::doMoreComputation);
    }
    private static Double timeMe(Supplier<Double> code) {

        Instant start = Instant.now();
        // Supplier method .get() just invokes whatever it is passed
        Double result = code.get();
        Instant end = Instant.now();

        Duration elapsed = Duration.between(start,end);
        System.out.println("Computation took:" + elapsed.toMillis());

        return result;
    }
}

0

Theo thuật ngữ Laymen,

nhà cung cấp sẽ cung cấp dữ liệu nhưng không sử dụng bất kỳ dữ liệu nào. Trong thuật ngữ lập trình, một phương thức không nhận bất kỳ đối số nào nhưng trả về một giá trị. Nó được sử dụng để tạo ra các giá trị mới.

http://codedestine.com/java-8-supplier-interface/

người tiêu dùng sẽ sử dụng dữ liệu và nhưng không trả lại bất kỳ dữ liệu nào. Trong thuật ngữ lập trình, một phương thức nhận nhiều đối số và không trả về bất kỳ giá trị nào.

http://codedestine.com/java-8-consumer-interface/


0

Người tiêu dùng và nhà cung cấp là giao diện được cung cấp bởi java. Người tiêu dùng được sử dụng để lặp lại các phần tử danh sách và nhà cung cấp được sử dụng cho đối tượng cung cấp

bạn có thể dễ dàng hiểu với trình diễn mã.

Khách hàng

package com.java.java8;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * The Class ConsumerDemo.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class ConsumerDemo {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {

    List<String> str = new ArrayList<>();
    str.add("DEMO");
    str.add("DEMO2");
    str.add("DEMO3");

    /* Consumer is use for iterate over the List */
    Consumer<String> consumer = new Consumer<String>() {
        @Override
        public void accept(String t) {

        /* Print list element on consile */
        System.out.println(t);
        }
    };

    str.forEach(consumer);

    }

}

Nhà cung cấp

package com.java.java8;

import java.util.function.Supplier;

/**
 * The Class SupplierDemo.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class SupplierDemo {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {
    getValue(() -> "Output1");
    getValue(() -> "OutPut2");
    }

    /**
     * Gets the value.
     *
     * @param supplier
     *            the supplier
     * @return the value
     */
    public static void getValue(Supplier<?> supplier) {
    System.out.println(supplier.get());
    }

}

0

Câu trả lời đơn giản nhất có thể là:

Người tiêu dùng có thể được xem như một Chức năng <T, Void>. Một Nhà cung cấp có thể được xem như một Chức năng <Void, T>.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.