Java 8でパラメーターとしてラムダを受け取るメソッドを定義するにはどうすればよいですか?


363

Java 8では、メソッドをLambda式として作成し、参照で渡すことができます(内部では少し作業が必要です)。オンラインでラムダを作成してメソッドで使用する例はたくさんありますが、ラムダをパラメーターとして取るメソッドの作成方法の例はありません。そのための構文は何ですか?

MyClass.method((a, b) -> a+b);


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}

29
良い質問。そして、あなたは正しい:どのチュートリアルもその部分を含んでいない。
マーティン

回答:


247

ラムダは純粋に呼び出しサイトの構造です。ラムダの受信者はラムダが関係していることを知る必要はなく、適切なメソッドでインターフェースを受け入れます。

言い換えると、必要なものを正確に受け入れて返す機能インターフェイス(つまり、単一のメソッドを持つインターフェイス)を定義または使用します。

このJava 8には、一般的に使用される一連のインターフェースタイプが付属していますjava.util.function(JavaDocのヒントを提供してくれたMaurice Naftalinに感謝します)。

この特定の使用例でjava.util.function.IntBinaryOperator、単一のint applyAsInt(int left, int right)メソッドがあるため、次のmethodように書くことができます。

static int method(IntBinaryOperator op){
    return op.applyAsInt(5, 10);
}

ただし、独自のインターフェースを定義して、次のように使用することもできます。

public interface TwoArgIntOperator {
    public int op(int a, int b);
}

//elsewhere:
static int method(TwoArgIntOperator operator) {
    return operator.op(5, 10);
}

独自のインターフェースを使用すると、意図をより明確に示す名前を付けることができるという利点があります。


5
使用する組み込みインターフェースはありますか、それとも取得したいラムダごとにインターフェースを作成する必要がありますか?
マリウス

再利用性と説明的な名前のジレンマに対する適切な妥協点は、指定したメソッドをオーバーライドせずに組み込みのインターフェースを拡張することです。これにより、コードが1行だけ追加されたわかりやすい名前が付けられます。
ウィルバーン

わかりません。彼は何のためにラムダを渡すことができ、それはうまくいきますか?彼が合格(int a, int b, int c)した場合はどうなりますかTwoArgIntOperator。同じシグネチャをTwoArgIntOperator持つ2つのメソッドがある場合はどうなりますか。この答えは紛らわしいです。
トマーシュZato -復活モニカ

7
@TomášZato:引数が一致しないラムダを使用すると、コンパイラーは文句を言うでしょう。また、2つの(デフォルトではない)メソッドを持つインターフェースは、機能的なインターフェースしか使用できないため、ラムダとして使用できません。
Joachim Sauer

基本的な質問:メソッドのパラメーターが欠落しているパラメーターとしてメソッドを渡す方法については、まだ理解できていないと思います。TwoArgIntOperatorをパラメーターとして渡していて、そのメソッドのパラメーターを個別に渡す必要がある場合、見栄えが悪くないですか?パラメータと共に完全な実行本体を渡す方法はありますか?あなたの例のように、「5」と「10」をハードコーディングするのを避ける方法。
instanceOfObject

63

Lambda式を使用するには、独自の機能インターフェイスを作成するか、2つの整数を必要とし、値として返される操作にJava関数インターフェイスを使用する必要があります。IntBinaryOperator

ユーザー定義の機能インターフェースの使用

interface TwoArgInterface {

    public int operation(int a, int b);
}

public class MyClass {

    public static void main(String javalatte[]) {
        // this is lambda expression
        TwoArgInterface plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));

    }
}

Java機能インターフェースの使用

import java.util.function.IntBinaryOperator;

public class MyClass1 {

    static void main(String javalatte[]) {
        // this is lambda expression
        IntBinaryOperator plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34));

    }
}

私が作成した他の例はここにあります


IntBinaryOperatorドキュメントへのリンクは無効です。
Hendrikto

1
これは私がこれまでに見つけたラムダの最良の例であり、本当にそれを「取得」させたのはそれだけでした。
JimmySmithJR 2016

1
Sooooooo ...基本的にはデリゲートですが、私たちはそれをそれと呼ぶことにしていませんか?
Ryan Lundy

37

3つ以上のパラメーターを持たない関数の場合、独自のインターフェースを定義せずにそれらを渡すことができます。例えば、

class Klass {
  static List<String> foo(Integer a, String b) { ... }
}

class MyClass{

  static List<String> method(BiFunction<Integer, String, List<String>> fn){
    return fn.apply(5, "FooBar");
  }
}

List<String> lStr = MyClass.method((a, b) -> Klass.foo((Integer) a, (String) b));

ではBiFunction<Integer, String, List<String>>IntegerおよびStringそのパラメータであり、List<String>その戻り値の型です。

パラメータが1つしかない関数の場合Function<T, R>、を使用できます。ここで、Tはそのパラメータタイプで、Rは戻り値のタイプです。Javaによってすでに利用可能になっているすべてのインターフェースについては、このページを参照してください


15

Lambda対応のJava 8 JavaDocsのWebアクセス可能なパブリックバージョンがあり、http: //lambdafaq.org/lambda-resourcesからリンクされています。(これは明らかにJoachim Sauerの回答に対するコメントであるはずですが、コメントを追加する必要があるレピュテーションポイントでSOアカウントにアクセスすることはできません。)lambdafaqサイト(私はそれを維持しています)はこれと他の多くのJavaに回答します-ラムダ質問。

注意:この回答は、Java 8 GAのドキュメントが一般公開される前に書かれました。ただし、Lambda FAQはJava 8で導入された機能について学ぶ人々にとって依然として役立つ可能性があるため、そのままにしておきます。


2
リンクとあなたがそのサイトを維持しているという事実をありがとう!私は自由にあなたの公開JavaDocへのリンクを私の答えに追加しました。
Joachim Sauer

1
余談ですが、Angelika LangerがGenerics用に構築したものをLambdas用に構築しているようです。そのおかげで、Javaにはそのようなリソースが必要です。
Joachim Sauer

1
このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。- レビューより
ClearLogic

@ClearLogicはい、同意しました。AFAIR既存の回答に何も追加したくはありませんでしたが、APIドキュメントのコピーを投稿した場所を指摘しただけでした。
モーリスナフタリン

7

私にとって、最も意味のある解決策は、Callbackインターフェイスを定義することです:

interface Callback {
    void call();
}

そして、それを呼び出したい関数のパラメータとして使用するには:

void somewhereInYourCode() {
    method(() -> {
        // You've passed a lambda!
        // method() is done, do whatever you want here.
    });
}

void method(Callback callback) {
    // Do what you have to do
    // ...

    // Don't forget to notify the caller once you're done
    callback.call();
}

ただ精度

ラムダは特別なインターフェイス、クラス、または自分で宣言できるものではありません。Lambdaは、() -> {}特別な構文に付けられた名前にすぎません。これにより、単一メソッドのインターフェースをパラメーターとして渡すときに読みやすくなります。これを置き換えるように設計されました:

method(new Callback() {
    @Override
    public void call() {
        // Classic interface implementation, lot of useless boilerplate code.
        // method() is done, do whatever you want here.
    }
});

上記の例でCallbackは、ラムダではなく、単なる通常のインターフェースです。lambda実装に使用できるショートカット構文の名前です。


6

これをグーグルする人にとって、良い方法はを使用することjava.util.function.BiConsumerです。例:

Import java.util.function.Consumer
public Class Main {
    public static void runLambda(BiConsumer<Integer, Integer> lambda) {
        lambda.accept(102, 54)
    }

    public static void main(String[] args) {
        runLambda((int1, int2) -> System.out.println(int1 + " + " + int2 + " = " + (int1 + int2)));
    }

アウトプリントは次のようになります:166


1
の代わりにConsumer<Pair<A,B>>BiConsumer<A,B>この場合に使用します。(ドキュメント
nobar 2018

存在することを知らなかったので、次回は関数パッケージをふるいにかける必要があります。
Big_Bad_E 2018年

5

ラムダ式は引数として渡すことができます。ラムダ式を引数として渡すには、パラメーターの型(ラムダ式を引数として受け取る)が機能的なインターフェイス型である必要があります。

機能的なインターフェースがある場合-

interface IMyFunc {
   boolean test(int num);
}

5より大きい場合にのみ、リストにintを追加するフィルターメソッドがあります。ここで、フィルターメソッドには、パラメーターの1つとして機能的なインターフェイスIMyFuncがあることに注意してください。その場合、ラムダ式をメソッドパラメータの引数として渡すことができます。

public class LambdaDemo {
    public static List<Integer> filter(IMyFunc testNum, List<Integer> listItems) {
        List<Integer> result = new ArrayList<Integer>();
        for(Integer item: listItems) {
            if(testNum.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    public static void main(String[] args) {
        List<Integer> myList = new ArrayList<Integer>();
        myList.add(1);
        myList.add(4);
        myList.add(6);
        myList.add(7);
        // calling filter method with a lambda expression
        // as one of the param
        Collection<Integer> values = filter(n -> n > 5, myList);

        System.out.println("Filtered values " + values);
    }
}

2

上記のように機能インターフェースを使用できます。以下はいくつかの例です

Function<Integer, Integer> f1 = num->(num*2+1);
System.out.println(f1.apply(10));

Predicate<Integer> f2= num->(num > 10);
System.out.println(f2.test(10));
System.out.println(f2.test(11));

Supplier<Integer> f3= ()-> 100;
System.out.println(f3.get());

それが役に立てば幸い


1

まあ、それは簡単です。ラムダ式の目的は、機能インターフェイスを実装することです。これは、メソッドが1つしかないインターフェースです。ここでは、事前定義されたレガシー機能インターフェースに関するすばらしい記事を紹介します。

とにかく、独自の機能的なインターフェイスを実装したい場合は、それを作成します。単純な例として:

public interface MyFunctionalInterface {
    String makeIt(String s);
}

MyFunctionalInterfaceの型を受け入れるメソッドを作成するクラスを作成しましょう。

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {

    }
}

最後に、定義したメソッドにMyFunctionalInterfaceの実装を渡す必要があります。

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {
        printIt("Java", s -> s + " is Awesome");
    }
}

それでおしまい!


1

Lambdaはオブジェクトではなく、機能インターフェースです。@FuntionalInterfaceを注釈として使用して、できるだけ多くの機能インターフェースを定義できます。

@FuntionalInterface
public interface SumLambdaExpression {
     public int do(int a, int b);
}

public class MyClass {
     public static void main(String [] args) {
          SumLambdaExpression s = (a,b)->a+b;
          lambdaArgFunction(s);
     }

     public static void lambdaArgFunction(SumLambdaExpression s) {
          System.out.println("Output : "+s.do(2,5));
     }
}

出力は次のようになります

Output : 7

ラムダ式の基本概念は、独自のロジックを定義することですが、すでに定義された引数です。したがって、上記のコードでは、do関数の定義を追加から他の定義に変更できますが、引数は2に制限されています。


1

基本的にラムダ式をパラメーターとして渡すには、それを保持できる型が必要です。整数値と同じように、プリミティブintまたはIntegerクラスで保持します。Javaはラムダ式用の個別のタイプを持たず、代わりに引数を保持するタイプとしてインターフェースを使用します。しかし、そのインターフェースは機能的なインターフェースでなければなりません。


0

以下をせよ ..

あなたは宣言したmethod(lambda l) 名前とのインタフェースを作成されてやりたいすべてをlambdaし、1つの抽象メソッドを宣言します

public int add(int a,int b);  

メソッド名はここでは関係ありません。

したがって、uを呼び出すと、MyClass.method( (a,b)->a+b) この実装 (a,b)->a+bはインターフェイスのaddメソッドに注入されます。したがって、呼び出すたびl.addに、この実装を取得してab andの追加を実行し、return l.add(2,3)を返し5ます。-基本的に、これはラムダが行うことです。


-1

C#がこの問題を大まかに処理する方法を次に示します(ただし、Javaコードとして表現されます)。このようなもので、ほとんどすべてのニーズに対応できます。

import static org.util.function.Functions.*;

public class Test {

    public static void main(String[] args)
    {
        Test.invoke((a, b) -> a + b);       
    }

    public static void invoke(Func2<Integer, Integer, Integer> func)
    {
        System.out.println(func.apply(5, 6));
    }
}

package org.util.function;

public interface Functions {

    //Actions:
    public interface Action {
        public void apply();
    }

    public interface Action1<T1> {
        public void apply(T1 arg1);
    }

    public interface Action2<T1, T2> {
        public void apply(T1 arg1, T2 arg2);
    }

    public interface Action3<T1, T2, T3> {
        public void apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Action4<T1, T2, T3, T4> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Action5<T1, T2, T3, T4, T5> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Action6<T1, T2, T3, T4, T5, T6> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Action7<T1, T2, T3, T4, T5, T6, T7> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Action8<T1, T2, T3, T4, T5, T6, T7, T8> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }

    //Functions:
    public interface Func<TResult> {
        public TResult apply();
    }

    public interface Func1<T1, TResult> {
        public TResult apply(T1 arg1);
    }

    public interface Func2<T1, T2, TResult> {
        public TResult apply(T1 arg1, T2 arg2);
    }

    public interface Func3<T1, T2, T3, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Func4<T1, T2, T3, T4, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Func5<T1, T2, T3, T4, T5, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Func6<T1, T2, T3, T4, T5, T6, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Func7<T1, T2, T3, T4, T5, T6, T7, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }
}

-2

パラメータとしてラムダを使用することには柔軟性があります。Javaで関数型プログラミングを可能にします。基本的な構文は

パラメータ-> method_body

以下は、関数インターフェース(ラムダを使用)をパラメーターとして取るメソッドを定義できる方法です。a。関数型インターフェース内で宣言されたメソッドを定義する場合、たとえば、関数型インターフェースは、から呼び出されるメソッドへの引数/パラメーターとして指定されます。main()

@FunctionalInterface
interface FInterface{
    int callMeLambda(String temp);
}


class ConcreteClass{

    void funcUsesAnonymousOrLambda(FInterface fi){
        System.out.println("===Executing method arg instantiated with Lambda==="));
    }

    public static void main(){
        // calls a method having FInterface as an argument.
        funcUsesAnonymousOrLambda(new FInterface() {

            int callMeLambda(String temp){ //define callMeLambda(){} here..
                return 0;
            }
        }
    }

/***********Can be replaced by Lambda below*********/
        funcUsesAnonymousOrLambda( (x) -> {
            return 0; //(1)
        }

    }

FInterface fi =(x)-> {return 0; };

funcUsesAnonymousOrLambda(fi);

ここでは、ラムダ式をインターフェイスに置き換える方法を示しています。

上記はラムダ式の特定の使用法を説明していますが、もっとあります。参照 ラムダ内のJava 8ラムダは、外側のラムダから変数を変更できません

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