คำอธิบายของซัพพลายเออร์และผู้บริโภค Java 8 สำหรับคนธรรมดา


105

ในฐานะโปรแกรมเมอร์ที่ไม่ใช่ Java ที่เรียนรู้ Java ฉันกำลังอ่านSupplierและConsumerเชื่อมต่ออยู่ในขณะนี้ และฉันไม่สามารถคาดเดาการใช้งานและความหมายของพวกเขาได้ คุณจะใช้อินเทอร์เฟซเหล่านี้เมื่อใดและทำไม ใครช่วยยกตัวอย่างคนธรรมดาง่ายๆให้ฉันหน่อยได้ไหม…ฉันพบว่าตัวอย่างเอกสารไม่กระชับเพียงพอสำหรับความเข้าใจของฉัน


4
แต่ละหน้าของเอกสาร API จะมีลิงก์ชื่อ "ใช้" ที่ด้านบนซึ่งคุณสามารถคลิกได้ConsumerและSupplierคุณยังสามารถค้นหาบทช่วยสอนสำหรับConsumer...
Holger

8
ฉันชอบคำตอบของ Stuart Marks และฉันคิดว่าคนส่วนใหญ่ที่ตอบด้านล่างพลาดประเด็นนี้ คำถามไม่ใช่ "วิธี" ในการเขียนซัพพลายเออร์ผู้บริโภคและหน้าที่ เป็น "ทำไม" ในโลกที่คุณต้องการ? สำหรับคนที่ไม่คุ้นเคยกับพวกเขาพวกเขาทำให้โค้ดซับซ้อนขึ้นมาก แต่ประโยชน์ของการใช้พวกเขายังไม่ชัดเจน
anton1980

เท่าที่ฉันเห็น (และฉันแบ่งปันความไม่พอใจของคุณด้วยคำอธิบายแบบสัมผัส) มันเป็นเพียงวิธีที่เรียบง่ายในการแยกทั้งประเภทวัตถุและการจัดการวัตถุจากวัตถุที่ใช้ในรหัส สิ่งนี้ช่วยให้สามารถประยุกต์ใช้รหัสเดียวกันนี้กับออบเจ็กต์ประเภทต่างๆได้โดยเพียงแค่กำหนดคลาสใหม่ที่แตกต่างกันและฉีดเข้าไปในอินเทอร์เฟซ Supplier และ Consumer ดังนั้นในระบบบันทึกข้อมูลของตำรวจจะใช้รหัสผิวเผินเดียวกันสำหรับผู้ต้องสงสัยทุกคน แต่การพิมพ์ครั้งสุดท้ายสำหรับแต่ละคนขึ้นอยู่กับการจำแนกประเภทของผู้ต้องสงสัยแต่ละคนเช่น 'พลเมือง', 'อนุ', 'larcen', 'อาชญากร', 'แข็ง', ฯลฯ
ลำต้น

คำตอบ:


97

นี่คือซัพพลายเออร์:

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

นี่คือผู้บริโภค:

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

ดังนั้นในแง่ของคนธรรมดาซัพพลายเออร์เป็นวิธีการที่ส่งคืนค่าบางอย่าง (เช่นเดียวกับค่าส่งคืน) ในขณะที่ผู้บริโภคเป็นวิธีการที่ใช้ค่าบางอย่าง (เช่นเดียวกับอาร์กิวเมนต์วิธีการ) และดำเนินการบางอย่างกับพวกเขา

สิ่งเหล่านี้จะเปลี่ยนเป็นสิ่งเหล่านี้:

// 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);

สำหรับการใช้งานตัวอย่างพื้นฐานจะเป็น: Stream#forEach(Consumer)วิธีการ ต้องใช้ผู้บริโภคซึ่งใช้องค์ประกอบจากสตรีมที่คุณกำลังทำซ้ำและดำเนินการบางอย่างกับแต่ละองค์ประกอบ อาจพิมพ์ได้

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

3
ดังนั้นซัพพลายเออร์จึงเป็นวิธีสร้างอินสแตนซ์ของวิธีการที่ส่งคืน 'บางสิ่ง'?
james emanon

3
@jamesemanon เป๊ะ. นั่นอาจเป็นการอ้างอิงวิธีการหรือแลมด้า
Rohit Jain

15
ประโยชน์ของสิ่งนี้มากกว่าการเรียกใช้เมธอดโดยตรงคืออะไร? เป็นเพราะซัพพลายเออร์สามารถทำหน้าที่เป็นเหมือนตัวกลางและส่งคืนค่า "ส่งคืน" ได้หรือไม่?
james emanon

1
Consumer <Integer, Integer> ไม่ถูกต้อง ผู้บริโภคมีพารามิเตอร์ประเภทเดียว
johnlemon

2
แต่ทำไมต้องสร้างสิ่งก่อสร้างเช่นนี้? ปัญหาใดที่แก้ไขได้โดยการมีใน Java
Trunk

179

เหตุผลที่คุณมีปัญหาในการเข้าใจความหมายของอินเทอร์เฟซที่ใช้งานได้เช่นjava.util.functionอินเทอร์เฟซที่กำหนดไว้ในที่นี้ไม่มีความหมายใด ๆ ! สิ่งเหล่านี้มีอยู่เพื่อแสดงโครงสร้างเป็นหลักไม่ใช่ความหมายหมาย

สิ่งนี้ผิดปกติสำหรับ Java API ส่วนใหญ่ Java API ทั่วไปเช่นคลาสหรืออินเทอร์เฟซมีความหมายและคุณสามารถพัฒนาแบบจำลองทางจิตสำหรับสิ่งที่แสดงถึงและใช้สิ่งนั้นเพื่อทำความเข้าใจการทำงานของมัน พิจารณาjava.util.Listตัวอย่าง A Listคือภาชนะของวัตถุอื่น ๆ พวกเขามีลำดับและดัชนี size()จำนวนของวัตถุที่มีอยู่ในรายการจะถูกส่งกลับโดย แต่ละวัตถุมีดัชนีอยู่ในช่วง 0..size-1 (รวม) อ็อบเจ็กต์ที่ดัชนีiสามารถเรียกดูได้โดยการเรียกlist.get(i)สามารถเรียกดูโดยโทรและอื่น ๆ

อินเทอร์เฟซที่ใช้งานjava.util.functionได้ไม่มีความหมายเช่นนั้น แต่เป็นอินเทอร์เฟซที่เป็นเพียงตัวแทนโครงสร้างของฟังก์ชันเช่นจำนวนอาร์กิวเมนต์จำนวนค่าที่ส่งคืนและ (บางครั้ง) ไม่ว่าอาร์กิวเมนต์หรือค่าที่ส่งคืนจะเป็นแบบดั้งเดิม ดังนั้นเราจึงมีสิ่งที่ต้องการFunction<T,R>ซึ่งหมายถึงฟังก์ชั่นที่ใช้อาร์กิวเมนต์เดียวของชนิดTและส่งกลับค่าของชนิดR แค่นั้นแหละ. ฟังก์ชั่นนั้นทำอะไร? มันสามารถทำอะไรก็ได้ ... ตราบใดที่ใช้อาร์กิวเมนต์เดียวและส่งกลับค่าเดียว นั่นเป็นเหตุผลที่ข้อกำหนดสำหรับFunction<T,R>มีค่าน้อยกว่า "แสดงถึงฟังก์ชันที่ยอมรับอาร์กิวเมนต์เดียวและสร้างผลลัพธ์"

เห็นได้ชัดว่าเมื่อเราเขียนโค้ดมันมีความหมายและความหมายนั้นต้องมาจากที่ไหนสักแห่ง ในกรณีของอินเทอร์เฟซการทำงานความหมายมาจากบริบทที่ใช้ อินเทอร์เฟซFunction<T,R>ไม่มีความหมายในการแยก อย่างไรก็ตามในjava.util.Map<K,V>API มีดังต่อไปนี้:

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

(สัญลักษณ์แทนเพื่อความกะทัดรัด)

อ่าการใช้Functionนี้เป็น "ฟังก์ชันการทำแผนที่" มันทำอะไร? ในบริบทนี้ถ้าkeyไม่มีอยู่ในแผนที่ฟังก์ชันการแม็ปจะถูกเรียกและส่งมอบคีย์และคาดว่าจะสร้างค่าและคู่คีย์ - ค่าที่เป็นผลลัพธ์จะถูกแทรกลงในแผนที่

ดังนั้นคุณจึงไม่สามารถดูข้อกำหนดสำหรับFunction(หรืออินเทอร์เฟซการทำงานอื่น ๆ สำหรับเรื่องนั้น) และพยายามแยกแยะว่าหมายถึงอะไร คุณต้องดูที่ที่ใช้ใน API อื่น ๆ เพื่อทำความเข้าใจความหมายและความหมายนั้นใช้กับบริบทนั้นเท่านั้น


4
โดยพื้นฐานแล้วมันก็เป็นเพียงประเภทเท่านั้น
Jack Guo

อาจเป็นข้อมูลที่เป็นประโยชน์อีกอย่างหนึ่งก็คืออินเทอร์เฟซการทำงานสามารถมีวิธีการใช้งานหลายวิธีที่สามารถเพิ่มพฤติกรรมให้กับโค้ดของคุณได้
Jhon Mario Lotero

28

A Supplierคือวิธีการใด ๆ ที่ไม่มีอาร์กิวเมนต์และส่งกลับค่า งานของมันคือการจัดหาตัวอย่างของชั้นเรียนที่คาดหวัง ตัวอย่างเช่นทุกการอ้างอิงถึงเมธอด 'getter' คือไฟล์Supplier

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

อ้างอิงวิธีการเช่นใช้เป็นตัวอย่างของmyClass::getCountSupplier<Integer>

A Consumerคือวิธีการใด ๆ ที่ใช้อาร์กิวเมนต์และไม่ส่งคืนค่าใด ๆ มีการเรียกใช้สำหรับผลข้างเคียง ในแง่ Java a Consumerเป็นสำนวนสำหรับvoidวิธีการ วิธีการ 'setter' เป็นตัวอย่างที่ดี:

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

อ้างอิงวิธีการเช่นใช้myClass::setCountเป็นตัวอย่างของและConsumer<Integer>IntConsumer

A Function<A,B>คือวิธีการใด ๆ ที่ใช้อาร์กิวเมนต์ประเภทหนึ่งและส่งกลับอีกประเภทหนึ่ง สิ่งนี้สามารถเรียกได้ว่าเป็น 'การเปลี่ยนแปลง' Function<A,B>ใช้เวลาและผลตอบแทนA Bสิ่งที่น่าสังเกตคือสำหรับค่าที่กำหนดของAฟังก์ชันควรส่งคืนค่าเฉพาะBเสมอ AและBในความเป็นจริงอาจเป็นประเภทเดียวกันดังต่อไปนี้:

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

การอ้างอิงวิธีการอินสแตนซ์myClass:addTwoคือ a Function<Integer, Integer>และToIntFunction<Integer>.

การอ้างอิงเมธอดคลาสไปยัง getter เป็นอีกตัวอย่างหนึ่งของฟังก์ชัน

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

อ้างอิงวิธีการเรียนของตนMyClass::getCountเป็นตัวอย่างของและFunction<MyClass,Integer>ToIntFunction<MyClass>


16

เหตุใดอินเทอร์เฟซสำหรับผู้บริโภค / ซัพพลายเออร์ / ฟังก์ชันการทำงานอื่น ๆ จึงถูกกำหนดไว้ในแพ็คเกจ java.util.function : Consumer และ Supplier เป็นสองอินเทอร์เฟซการทำงานที่สร้างขึ้นใน Java 8 จุดประสงค์ของอินเทอร์เฟซการทำงานในตัวทั้งหมดนี้คือ เพื่อจัดเตรียม "เทมเพลต" ที่พร้อมใช้งานสำหรับอินเทอร์เฟซการทำงานที่มีตัวบอกฟังก์ชันทั่วไป (ลายเซ็น / คำจำกัดความของวิธีการทำงาน)

สมมติว่าเรามีความจำเป็นในการแปลงประเภท T เป็นประเภทอื่น R หากเราจะส่งฟังก์ชันใด ๆ ที่กำหนดไว้เช่นนี้เป็นพารามิเตอร์ไปยังวิธีการวิธีนั้นจะต้องกำหนดส่วนต่อประสานฟังก์ชันซึ่งวิธีการทำงาน / นามธรรมรับพารามิเตอร์ ประเภท T เป็นอินพุตและให้พารามิเตอร์ประเภท R เป็นเอาต์พุต ตอนนี้อาจมีหลายสถานการณ์เช่นนี้และโปรแกรมเมอร์จะต้องกำหนดอินเทอร์เฟซที่ใช้งานได้หลากหลายสำหรับความต้องการของพวกเขา เพื่อหลีกเลี่ยงสถานการณ์เช่นนี้ให้ง่ายต่อการเขียนโปรแกรมและนำมาตรฐานทั่วไปมาใช้ในการใช้งานอินเทอร์เฟซการทำงานชุดของอินเทอร์เฟซการทำงานที่สร้างขึ้นเช่น Predicate, Function, Consumer & Supplier ได้รับการกำหนด

Consumer ทำอะไร : อินเทอร์เฟซการทำงานของผู้บริโภคยอมรับอินพุตทำบางอย่างกับอินพุตนั้นและไม่ให้เอาต์พุตใด ๆ คำจำกัดความเป็นเช่นนี้ (จาก Java Source) -

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

ที่นี่ยอมรับ () เป็นวิธีการทำงาน \ นามธรรมซึ่งรับอินพุตและไม่ส่งคืนเอาต์พุต ดังนั้นหากคุณต้องการป้อนจำนวนเต็มให้ทำอะไรบางอย่างโดยไม่มีเอาต์พุตแทนที่จะกำหนดอินเทอร์เฟซของคุณเองให้ใช้อินสแตนซ์ของ Consumer

ซัพพลายเออร์ทำอะไร : อินเทอร์เฟซการทำงานของซัพพลายเออร์ไม่รับอินพุตใด ๆ แต่ส่งคืนเอาต์พุต กำหนดไว้เช่นนี้ (จาก Java Source) -

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

เมื่อใดก็ตามที่คุณต้องการฟังก์ชันที่ส่งคืนบางสิ่งให้พูดว่า Integer แต่ไม่ต้องใช้ผลลัพธ์ใด ๆ ให้ใช้อินสแตนซ์ของ Supplier

ในกรณีที่มีความชัดเจนมากขึ้นพร้อมกับตัวอย่างการใช้งานอินเทอร์เฟซสำหรับผู้บริโภคและซัพพลายเออร์คุณสามารถอ้างอิงบล็อกโพสต์ของฉันได้ที่http://www.javabrahman.com/java-8/java-8-java-util- function-consumer-tutorial-with-samples / และ http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/


12

1. ความหมาย

ดูคำตอบของฉันสำหรับคำถามของฉันที่นี่และคำถามอื่น ๆที่นี่แต่ในระยะสั้นอินเทอร์เฟซใหม่เหล่านี้ให้การประชุมและคำอธิบายสำหรับทุกคนที่จะใช้ (+ การผูกมัดวิธีการที่ขี้ขลาดเช่น.forEach(someMethod().andThen(otherMethod()))

2. ความแตกต่าง

ผู้บริโภค : ใช้บางอย่างทำบางอย่างไม่ส่งคืนอะไร:void accept(T t)

ซัพพลายเออร์: ไม่ได้อะไรเลยส่งคืนบางสิ่ง: T get()(ย้อนกลับของผู้บริโภคโดยทั่วไปเป็นวิธี 'getter' สากล)

3. การใช้งาน

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

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

ซัพพลายเออร์: ห่อรหัสซ้ำเช่นเวลาเรียกใช้รหัส

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

ในแง่ฆราวาส

ซัพพลายเออร์จะจัดหาข้อมูล แต่ไม่ใช้ข้อมูลใด ๆ ในแง่การเขียนโปรแกรมวิธีการที่ไม่ใช้อาร์กิวเมนต์ใด ๆ แต่ส่งคืนค่า ใช้เพื่อสร้างค่าใหม่

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

ผู้บริโภคจะใช้ข้อมูลและไม่ส่งคืนข้อมูลใด ๆ ในแง่การเขียนโปรแกรมวิธีการที่ใช้อาร์กิวเมนต์หลายรายการและไม่ส่งคืนค่าใด ๆ

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


0

ผู้บริโภคและซัพพลายเออร์เป็นอินเทอร์เฟซที่จัดทำโดย java ผู้บริโภคใช้เพื่อทำซ้ำองค์ประกอบรายการและซัพพลายเออร์ใช้สำหรับวัตถุอุปทาน

คุณสามารถเข้าใจได้อย่างง่ายดายด้วยการสาธิตรหัส

ผู้บริโภค

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);

    }

}

ผู้ผลิต

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());
    }

}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.