素人向けのJava 8サプライヤーと消費者の説明


101

Javaを学んでいる非Javaプログラマーとして、私は現在SupplierConsumerインターフェースとインターフェースについて読んでいます。そして、私は彼らの用法と意味に頭を抱えることはできません。いつ、なぜこれらのインターフェースを使用するのですか?誰かが私にこれの簡単な素人の例を教えてもらえますか?私の理解するのに十分ではないDocの例を見つけています。


4
APIのドキュメントあなたがクリックすることができます上部にある「USE」というリンクがあるの各ページConsumerSupplierあなたも検索することがチュートリアルのためにConsumer...
ホルガー

7
スチュアートマークスの答えが大好きです。そして、以下に答えたほとんどの人は要点を逃したと思います。問題は、サプライヤー、消費者、機能を「どのように」書くかではありません。世界で「なぜ」したいですか?彼らに慣れていない人にとって、彼らはコードをはるかに複雑にします。しかし、それらを使用するメリットは明確ではありません。
anton1980

私が見る限り(そして私があなたの不満を正接の説明と共有している限り)、これは、コードで使用されるオブジェクトからオブジェクトタイプとオブジェクト処理の両方を抽象化する洗練された方法にすぎません。これにより、さまざまな新しいクラスを定義し、それらをサプライヤーインターフェイスとコンシューマーインターフェイスに挿入するだけで、この同じコードをさまざまな種類のオブジェクトに適用できます。したがって、警察の記録システムでは、すべての容疑者に同じ表面的なコードが使用されますが、それぞれの最終的な出力は、各容疑者の分類に依存します。など
トランク

回答:


95

これはサプライヤーです:

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 2015

3
@jamesemanonまさに。それはメソッド参照またはラムダかもしれません。
Rohit Jain、

14
メソッドを直接呼び出すよりも、これの利点は何ですか?それは、サプライヤーが仲介者のように振る舞い、その「戻り」の値を引き渡すことができるからですか?
james emanon 2015

1
Consumer <Integer、Integer>は無効です。コンシューマーには単一の型パラメーターがあります。
ナスカー

2
しかし、なぜそのような構造を作成するのですか?Javaでそれを持っていることによってどのような問題が解決されますか?
トランク

178

のような機能的なインターフェースの意味を理解するのが難しい理由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型の値を返す関数を表すようなものがあります。それでおしまい。その機能は何をしますか?まあ、1つの引数を取り、1つの値を返す限り、何でもできます。それがの仕様Function<T,R>が「1つの引数を受け入れて結果を生成する関数を表す」に過ぎない理由です。

明らかに、私たちがコードを書いているとき、それには意味があり、その意味はどこかから来ている必要があります。機能的なインターフェースの場合、意味はそれらが使用されるコンテキストに由来します。インターフェースFunction<T,R>は単独では意味がありません。ただし、java.util.Map<K,V>APIには次のものがあります。

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

(簡潔にするために省略されたワイルドカード)

ああ、この使用はFunction「マッピング機能」です。それは何をしますか?このコンテキストkeyで、がまだマップに存在しない場合、マッピング関数が呼び出され、キーが渡され、値を生成することが期待され、結果のキーと値のペアがマップに挿入されます。

したがって、仕様Function(またはその他の機能的インターフェース)を見て、それらの意味を理解することはできません。それらが他のAPIでどこで使用されているかを見て、それらの意味を理解する必要があります。その意味はそのコンテキストにのみ適用されます。


3
つまり、基本的にはタイプとして機能するだけです
JGuo

もう1つの有用な情報は、関数型インターフェイスに、コードに動作を追加できる複数の実装されたメソッドがある可能性があることです
Jhon Mario Lotero

28

A Supplierは、引数を取らず、値を返す任意のメソッドです。その仕事は文字通り、期待されるクラスのインスタンスを提供することです。たとえば、「getter」メソッドへのすべての参照はSupplier

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

そのインスタンスメソッド参照myClass::getCountはのインスタンスですSupplier<Integer>

A Consumerは引数を取り、何も返さないメソッドです。副作用のために呼び出されます。Java用語では、a Consumervoidメソッドのイディオムです。'setter'メソッドは良い例です:

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

そのインスタンスメソッド参照myClass::setCountは、Consumer<Integer>およびのインスタンスですIntConsumer

A Function<A,B>は、ある型の引数を取り、別の型を返す任意のメソッドです。これは「変換」と呼ぶことができます。はをFunction<A,B>取り、Aを返しますB。注目すべきは、指定された値に対してA、関数が常に特定の値を返す必要があることですBAそしてB実際には、次のような同じタイプであることができます。

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

そのインスタンスメソッド参照myClass:addTwoはa Function<Integer, Integer>およびToIntFunction<Integer>です。

getterへのClassメソッド参照は、関数のもう1つの例です。

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

そのクラスメソッド参照MyClass::getCountFunction<MyClass,Integer>およびのインスタンスですToIntFunction<MyClass>


15

なぜコンシューマー/サプライヤー/その他の機能インターフェースがjava.util.functionパッケージで定義されているのか:コンシューマーとサプライヤーは、Java 8で提供される組み込み機能インターフェースの2つです。これらすべての組み込み機能インターフェースの目的は、共通の関数記述子(関数メソッドのシグネチャ/定義)を持つ関数インターフェイスの準備が整った「テンプレート」を提供します。

我々は我々が合格した場合、別のタイプR.に型Tに変換するために必要としていたとしましょう任意の方法は、その官能/抽象メソッドのパラメータをとる機能インタフェースを定義する必要があること、次に、メソッドのパラメータとして、次のように定義された関数をタイプTの入力として、タイプRのパラメーターを出力として提供します。今、このような多くのシナリオがあり、プログラマーは最終的に彼らのニーズのために複数の機能的なインターフェースを定義することになります。この種のシナリオを回避し、プログラミングを容易にし、機能インターフェースの使用に共通の標準をもたらすために、述語、関数、コンシューマー、サプライヤーなどの組み込みの機能インターフェースのセットが定義されています。

コンシューマーは何をしますか:コンシューマー機能インターフェイスは入力を受け入れ、その入力で何かを行い、出力を提供しません。その定義は次のようになります(Javaソースから)-

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

ここで、accept()は、入力を受け取り、出力を返さないfunction \ abstractメソッドです。そのため、Integerを入力する場合は、出力なしでそれを使って何かを実行してから、独自のインターフェースを定義する代わりに、Consumerのインスタンスを使用します。

サプライヤーは何をしますか:サプライヤー機能インターフェースは入力を取りませんが、出力を返します。このように定義されています(Javaソースから)-

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

Integerなど、何かを返すが、出力を取得しない関数が必要な場合は、Supplierのインスタンスを使用します。

コンシューマーとサプライヤーのインターフェイスの使用例とともに、より明確にする必要がある場合は、同じブログ記事を参照できます-http://www.javabrahman.com/java-8/java-8-java-util- function-consumer-tutorial-with-examples / および 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()消費者の逆、基本的には普遍的な「ゲッター」方法)

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

Laymen用語では、

サプライヤーはデータを提供しますが、データを消費しません。プログラミング用語では、引数をとらずに値を返すメソッド。新しい値を生成するために使用されます。

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

}

0

最も簡単な答えは次のとおりです。

コンシューマーはFunction <T、Void>と見なすことができます。サプライヤは、Function <Void、T>として表示できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.