मैं एक विधि को कैसे परिभाषित करता हूं जो जावा 8 में एक पैरामीटर के रूप में एक लैम्ब्डा लेता है?


363

जावा 8 में, तरीकों को लैम्ब्डा अभिव्यक्तियों के रूप में बनाया जा सकता है और संदर्भ द्वारा पारित किया जा सकता है (हुड के तहत थोड़ा काम के साथ)। लैम्ब्डा बनाने और उपयोग करने के तरीकों के साथ ऑनलाइन बहुत सारे उदाहरण हैं, लेकिन एक लैम्बडा को एक पैरामीटर के रूप में लेने के तरीके का कोई उदाहरण नहीं है। उसके लिए वाक्य रचना क्या है?

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

लैम्ब्डा विशुद्ध रूप से एक कॉल-साइट निर्माण है: लैम्ब्डा के प्राप्तकर्ता को यह जानने की आवश्यकता नहीं है कि लैम्बडा शामिल है, इसके बजाय यह उपयुक्त विधि के साथ एक इंटरफ़ेस स्वीकार करता है।

दूसरे शब्दों में, आप एक कार्यात्मक इंटरफ़ेस को परिभाषित करते हैं या इसका उपयोग करते हैं (यानी एक एकल विधि के साथ एक इंटरफ़ेस) जिसे आप स्वीकार करते हैं और वास्तव में वही चाहते हैं जो आप चाहते हैं।

इसके लिए जावा 8 सामान्य रूप से उपयोग किए जाने वाले इंटरफ़ेस प्रकारों के एक सेट के साथ आता है java.util.function( जावाडॉक के बारे में संकेत के लिए मौरिस नेफ्टलिन के लिए धन्यवाद )।

इस विशिष्ट उपयोग के लिए वहाँ 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
क्या उपयोग किए जाने वाले अंतर्निर्मित इंटरफेस होंगे, या मुझे जो भी लैम्बडा मैं लेना चाहते हैं, उसके लिए एक इंटरफ़ेस बनाना होगा?
मारियस

पुन: प्रयोज्य बनाम वर्णनात्मक नाम दुविधा के लिए एक अच्छा समझौता निर्दिष्ट किए गए तरीके को ओवरराइड किए बिना इंटरफ़ेस में निर्मित का विस्तार करना होगा। यह आपको कोड की केवल एक अतिरिक्त पंक्ति के साथ आपका वर्णनात्मक नाम देता है।
विल बर्न

मुझे नहीं मिला। वह किसी भी चीज के लिए लंबोदा पास कर सकता है और यह काम करेगा? अगर वह गुजरता है तो क्या होता है (int a, int b, int c)के लिए TwoArgIntOperator। क्या होता है अगर TwoArgIntOperatorहै दो समान हस्ताक्षर से तरीकों। यह उत्तर भ्रमित करने वाला है।
टॉम ज़ातो -

7
@ TomáZZato: यदि आप एक लैंबडा का उपयोग गैर-मिलान वाले तर्कों के साथ करते हैं तो कंपाइलर शिकायत करेगा। और दो (गैर-डिफ़ॉल्ट) विधियों के साथ इंटरफेस लैम्ब्डा के रूप में उपयोग करने योग्य नहीं होगा, क्योंकि केवल कार्यात्मक इंटरफेस का उपयोग किया जा सकता है।
जोकिम सॉरे

एक मूल प्रश्न: मुझे लगता है कि मैं अभी भी उस पद्धति के मापदंडों के साथ एक विधि को पारित करने के पहलू को नहीं समझता, जो अनुपलब्ध है। यदि वह TwoArgIntOperator को एक पैरामीटर के रूप में पारित कर रहा है और उसे उस पद्धति के पैरामीटर को अलग से पारित करने की आवश्यकता है, तो क्या यह बदसूरत नहीं है? क्या पैरामीटर के साथ पूर्ण निष्पादन निकाय को पारित करने का एक तरीका है? जैसे आपके उदाहरण में, "5" और "10" हार्डकोडिंग से बचने का एक तरीका।
instanceOfObject

63

लैम्ब्डा अभिव्यक्ति का उपयोग करने के लिए आपको या तो अपना स्वयं का कार्यात्मक इंटरफ़ेस बनाना होगा या ऑपरेशन के लिए जावा फंक्शनल इंटरफ़ेस का उपयोग करना होगा, जिसके लिए दो पूर्णांक की आवश्यकता होती है और मान के रूप में वापस आता है। 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));

    }
}

जावा कार्यात्मक इंटरफ़ेस का उपयोग करना

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दस्तावेज़ का लिंक मर चुका है।
हेंड्रिक्टो

1
यह लैम्बडा का अब तक का सबसे अच्छा उदाहरण है, और यह केवल एक ही था जो मुझे वास्तव में मिला था।
जिम्मीस्मिथराज

1
Sooooooo ... मूल रूप से एक प्रतिनिधि, लेकिन हम इसे कहते हैं कि नहीं चाहिए?
रयान लुन्डी

37

ऐसे फ़ंक्शंस के लिए जिनमें 2 से अधिक पैरामीटर नहीं हैं, आप अपने स्वयं के इंटरफ़ेस को परिभाषित किए बिना उन्हें पास कर सकते हैं। उदाहरण के लिए,

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>इसकी वापसी प्रकार है।

केवल एक पैरामीटर वाले फ़ंक्शन के लिए, आप उपयोग कर सकते हैं Function<T, R>, जहां Tइसका पैरामीटर प्रकार है, और Rइसका रिटर्न मान प्रकार है। जावा द्वारा पहले ही उपलब्ध कराए गए सभी इंटरफेस के लिए इस पृष्ठ का संदर्भ लें ।


15

Http://lambdafaq.org/lambda-resources से जुड़े लैम्ब्डा-सक्षम जावा 8 जावाडॉक्स का एक सार्वजनिक वेब-सुलभ संस्करण है । (यह स्पष्ट रूप से जोआचिम सॉर के जवाब पर एक टिप्पणी होनी चाहिए, लेकिन मैं अपने एसओ खाते में उन प्रतिष्ठा बिंदुओं के साथ नहीं जा सकता हूं जिन्हें मुझे एक टिप्पणी जोड़ने की आवश्यकता है।) लैम्ब्डाफाक साइट (मैं इसे बनाए रखता हूं) इसका जवाब देता हूं और बहुत सारे अन्य जावा। -लांबा प्रश्न

NB यह उत्तर जावा 8 GA प्रलेखन सार्वजनिक रूप से उपलब्ध होने से पहले लिखा गया था । हालांकि, मैंने जगह छोड़ दी है, क्योंकि लैम्बडा एफएक्यू अभी भी जावा 8 में शुरू की गई सुविधाओं के बारे में जानने वाले लोगों के लिए उपयोगी हो सकता है।


2
लिंक और उस साइट को बनाए रखने के लिए धन्यवाद! मैंने अपने उत्तर के लिए आपके सार्वजनिक JavaDoc से लिंक जोड़ने की स्वतंत्रता ली।
जोकिम सॉउर

1
एक साइड नोट के रूप में: ऐसा लगता है कि आप लैम्बडा के लिए निर्माण कर रहे हैं जो एंजेलिका लैंगर ने जेनरिक के लिए बनाया है । इसके लिए धन्यवाद, जावा को ऐसे संसाधनों की आवश्यकता है!
जोकिम सॉउर

1
हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं। - समीक्षा से
ClearLogic

@ClearLogic हाँ, सहमत हैं। AFAIR मैं मौजूदा उत्तरों के लिए कुछ भी जोड़ना नहीं चाहता था, लेकिन केवल यह बताने के लिए कि मैंने एपीआई प्रलेखन की एक प्रति कहां पोस्ट की थी, जो उस समय अन्यथा आसानी से सुलभ नहीं थी।
मौरिस नेफ्तालिन

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

पता नहीं था कि अस्तित्व में है, मुझे अगली बार फ़ंक्शन पैकेज के माध्यम से झारना चाहिए।
Big_Bad_E

5

लैम्ब्डा अभिव्यक्ति को एक तर्क के रूप में पारित किया जा सकता है। एक लैम्बडा अभिव्यक्ति को एक तर्क के रूप में पास करें जो पैरामीटर के प्रकार (जो एक तर्क के रूप में लैम्ब्डा अभिव्यक्ति प्राप्त करता है) कार्यात्मक इंटरफ़ेस प्रकार का होना चाहिए।

यदि एक कार्यात्मक इंटरफ़ेस है -

interface IMyFunc {
   boolean test(int num);
}

और एक फ़िल्टर विधि है जो सूची में केवल इंट को जोड़ता है यदि यह 5 से अधिक है। यहां ध्यान दें कि फ़िल्टर विधि में पैरामीटर में से एक के रूप में फ़ंतासी इंटरफ़ेस 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

खैर, यह आसान है। लंबोदर अभिव्यक्ति का उद्देश्य कार्यात्मक इंटरफ़ेस को लागू करना है। यह केवल एक विधि वाला इंटरफ़ेस है। यहाँ पूर्वनिर्धारित और विरासत कार्यात्मक इंटरफेस के बारे में awesone लेख है।

वैसे भी, यदि आप अपने स्वयं के कार्यात्मक इंटरफ़ेस को लागू करना चाहते हैं, तो इसे बनाएं। बस सरल उदाहरण के लिए:

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

लैम्ब्डा एक वस्तु नहीं है, बल्कि एक कार्यात्मक इंटरफ़ेस है। कोई भी कार्यात्मक इंटरफेस के रूप में कई परिभाषित कर सकता है क्योंकि वे एनोटेशन के रूप में @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 वर्ग में रखते हैं। जावा में लैम्डा अभिव्यक्ति के लिए एक अलग प्रकार नहीं है, इसके बजाय यह तर्क को रखने के लिए एक इंटरफ़ेस का उपयोग करता है। लेकिन वह इंटरफ़ेस एक कार्यात्मक इंटरफ़ेस होना चाहिए ।


0

निम्न कार्य करें ..

आपने घोषणा की है method(lambda l) कि आप जो भी करना चाहते हैं, वह नाम के साथ एक इंटरफ़ेस बना रहा है lambdaऔर एक सार पद्धति की घोषणा करता है

public int add(int a,int b);  

विधि नाम यहाँ मायने नहीं रखता है ।।

इसलिए जब यू फोन MyClass.method( (a,b)->a+b) इस कार्यान्वयन (a,b)->a+bअपने इंटरफेस ऐड विधि तो करने के लिए इंजेक्शन दिया जाएगा जब भी आप कहते हैं l.addकि यह इस कार्यान्वयन लेने के लिए और के अलावा प्रदर्शन करने के लिए जा रहा है aऔर b और return l.add(2,3)वापस आ जाएगी 5। - मूल रूप से यह वही है जो लंबोदर करता है ।।


-1

यहाँ मोटे तौर पर C # इस समस्या को कैसे हैंडल करता है (लेकिन जावा कोड के रूप में व्यक्त किया गया है)। ऐसा कुछ आपकी लगभग सभी जरूरतों को पूरा कर सकता है:

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

लैम्बडा को पैरामीटर के रूप में उपयोग करने में लचीलापन है। यह जावा में कार्यात्मक प्रोग्रामिंग को सक्षम बनाता है। मूल वाक्य रचना है

परम -> method_body

निम्नलिखित एक तरीका है, आप पैरामीटर के रूप में कार्यात्मक इंटरफ़ेस (लैम्डा का उपयोग किया जाता है) लेने वाली एक विधि को परिभाषित कर सकते हैं। ए। यदि आप एक कार्यात्मक इंटरफ़ेस के भीतर घोषित एक विधि को परिभाषित करना चाहते हैं, तो कहते हैं, कार्यात्मक इंटरफ़ेस को एक विधि के लिए एक तर्क / पैरामीटर के रूप में दिया जाता है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)
        }

    }

Fterterface Fi = (x) -> {वापसी 0; };

funcUsesAnonymousOrLambda (इंटरनेट);

यहाँ ऊपर यह देखा जा सकता है कि एक लैम्ब्डा अभिव्यक्ति को एक इंटरफ़ेस से कैसे बदला जा सकता है।

ऊपर लैंबडा अभिव्यक्ति के एक विशेष उपयोग के बारे में बताते हैं, और भी हैं। रेफरी जावा 8 लैम्ब्डा के भीतर एक लैम्ब्डा बाहरी लैम्ब्डा से वेरिएबल को संशोधित नहीं कर सकता है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.