जावा 8 में कौन से कार्यात्मक इंटरफेस का उपयोग किया जाता है?


154

मुझे जावा 8 में एक नया शब्द आया: "कार्यात्मक इंटरफ़ेस"। मैं लंबोदर भावों के साथ काम करते हुए इसका केवल एक ही उपयोग कर पाया ।

जावा 8 कुछ अंतर्निहित कार्यात्मक इंटरफेस प्रदान करता है और अगर हम किसी भी कार्यात्मक इंटरफ़ेस को परिभाषित करना चाहते हैं तो हम @FunctionalInterfaceएनोटेशन का उपयोग कर सकते हैं । यह हमें इंटरफ़ेस में केवल एक ही विधि घोषित करने की अनुमति देगा।

उदाहरण के लिए:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

जावा 8 में लैंबडा एक्सप्रेशन के साथ काम करने के अलावा यह कितना उपयोगी है ?

( यहाँ प्रश्न मेरे द्वारा पूछे गए प्रश्न से भिन्न है। यह पूछ रहा है कि लैम्ब्डा एक्सप्रेशन के साथ काम करते समय हमें कार्यात्मक इंटरफेस की आवश्यकता क्यों है। मेरा सवाल है: लैम्बडा एक्सप्रेशंस के अलावा अन्य कार्यात्मक इंटरफेस का उपयोग क्यों होता है?)


1
यह इस लिंक से जुड़ता हुआ दिख रहा है। वे इस बारे में भी बात करते हैं कि कार्यात्मक इंटरफ़ेस में केवल एक ही तरीका क्यों होना चाहिए। stackoverflow.com/questions/33010594/…
कुलभूषण सिंह

1
@ कुलभूषण सिंह ने पोस्टिंग से पहले यह प्रश्न देखा ... दोनों प्रश्नों में अंतर है ...
मधुसूदन

जवाबों:


127

@FunctionalInterfaceएनोटेशन आपके कोड के संकलन समय की जाँच के लिए उपयोगी है। आपके पास इसके अलावा एक से अधिक विधि नहीं हो सकती है static, defaultऔर अमूर्त विधियां जो Objectआपके @FunctionalInterfaceया किसी अन्य इंटरफ़ेस में विधियों को एक कार्यात्मक इंटरफ़ेस के रूप में ओवरराइड करती हैं ।

लेकिन आप इस एनोटेशन के बिना लैम्बदास का उपयोग कर सकते हैं और साथ ही आप @Overrideएनोटेशन के बिना तरीकों को ओवरराइड कर सकते हैं ।

डॉक्स से

एक कार्यात्मक इंटरफ़ेस में एक सार तरीका है। चूंकि डिफ़ॉल्ट विधियों का कार्यान्वयन होता है, वे अमूर्त नहीं होते हैं। यदि कोई इंटरफ़ेस java.lang.Object के सार्वजनिक तरीकों में से एक को ओवरराइड करने के लिए एक अमूर्त विधि की घोषणा करता है, तो यह भी इंटरफ़ेस की सार पद्धति की गणना नहीं करता है क्योंकि इंटरफ़ेस के किसी भी कार्यान्वयन में java.lang.bject या कहीं और से कार्यान्वयन होगा।

इसका उपयोग लंबोदर अभिव्यक्ति में किया जा सकता है :

public interface Foo {
  public void doSomething();
}

इसका उपयोग लंबोदर अभिव्यक्ति में नहीं किया जा सकता है :

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

लेकिन यह संकलन त्रुटि देगा :

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

अमान्य '@FunctionalInterface' एनोटेशन; फू एक कार्यात्मक इंटरफ़ेस नहीं है


43
अधिक सटीक होने के लिए, आपके पास बिल्कुल एक अमूर्त तरीका होना चाहिए जो java.lang.Objectएक कार्यात्मक इंटरफ़ेस में एक विधि को ओवरराइड नहीं करता है।
होल्गर

9
... और यह "एक से अधिक नहीं करने के लिए थोड़ा अलग है publicविधि के अलावा staticऔर default" ...
होल्गर

4
अभी भी इसे होने के किसी भी बिंदु को समझ में नहीं आता है। पृथ्वी पर कोई भी जाँच करेगा कि उसके इंटरफ़ेस में कितने तरीके हैं। मार्कर इंटरफेस में अभी भी एक बिंदु और एक विशिष्ट उद्देश्य है। प्रलेखन और उत्तर केवल यह बताता है कि यह क्या करता है, न कि यह किसी भी तरह से कैसे उपयोग किया जाता है। और "उपयोग" बिल्कुल वही है जो ओपी ने पूछा था। इसलिए मैं इस जवाब की सिफारिश नहीं करूंगा।
saran3h

1
@VNT संकलन त्रुटि को इस इंटरफ़ेस के क्लाइंट मिलते हैं, लेकिन इंटरफ़ेस ही नहीं बदल सकता है। इस एनोटेशन के साथ संकलन त्रुटि इंटरफ़ेस पर है, इसलिए आप यह सुनिश्चित करते हैं कि कोई भी आपके इंटरफ़ेस के क्लाइंट को नहीं तोड़ देगा।
सर्गी बिशप

2
यह दिखाता है कि उनका उपयोग कैसे किया जाए, लेकिन यह नहीं बताया गया कि हमें उनकी आवश्यकता क्यों है।
शेख

14

प्रलेखन वास्तव में उद्देश्य के बीच एक अंतर बना देता है

एक सूचनात्मक एनोटेशन प्रकार का उपयोग यह इंगित करने के लिए किया जाता है कि एक इंटरफ़ेस प्रकार की घोषणा का उद्देश्य एक कार्यात्मक इंटरफ़ेस है जैसा कि जावा भाषा विनिर्देश द्वारा परिभाषित किया गया है।

और उपयोग मामला

ध्यान दें कि कार्यात्मक इंटरफेस के उदाहरण लंबोदर भाव, विधि संदर्भ, या निर्माता संदर्भ के साथ बनाए जा सकते हैं।

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

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

JLS §9.8। कार्यात्मक इंटरफेस :

...

क्लास (.915.9) घोषित करके और इंस्टेंट करके एक इंटरफ़ेस उदाहरण बनाने की सामान्य प्रक्रिया के अलावा, कार्यात्मक संदर्भों के उदाहरण विधि संदर्भ अभिव्यक्ति और लैम्ब्डा एक्सप्रेशन (.115.13, .215.27) के साथ बनाए जा सकते हैं।

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

तो एक वाक्य में, नहीं, जावा 8 में इसके लिए कोई अन्य उपयोग मामला नहीं है।


बस थोड़ा बहुत या अप्रासंगिक होने के लिए कह सकते हैं (आप जवाब देने के लिए नहीं चुन सकते हैं), लेकिन जब आप किसी व्यक्ति ने public static String generateTaskId()इसे अधिक "कार्यात्मक" बनाने के लिए किसी और को "कार्यात्मक" बनाने public class TaskIdSupplier implements Supplier<String>के लिए getविधि का उपयोग करके इसे लिखने के लिए चुना है , तो आप क्या सुझाव देंगे । मौजूदा पीढ़ी के कार्यान्वयन। क्या यह कार्यात्मक इंटरफेस का दुरुपयोग है, खासकर SupplierJDK बिल्ट-इन से पुन: उपयोग ? पुनश्च: मुझे यह पूछने के लिए एक बेहतर स्थान / क्यू और ए नहीं मिला। यदि आप सुझाव दे सकें तो प्रवास करने में प्रसन्नता होगी।
नमन

1
@ नाम जब आप एक नामित वर्ग बनाते हैं तो आप उपयोगिता पद्धति को अधिक कार्यात्मक नहीं बना रहे हैं TaskIdSupplier। अब, सवाल यह है कि आपने नामित वर्ग क्यों बनाया। ऐसे परिदृश्य हैं जहां इस तरह के नामित प्रकार की आवश्यकता होती है, उदाहरण के लिए जब आप कार्यान्वयन को खोजने का समर्थन करना चाहते हैं ServiceLoader। इसे लागू करने देने में कुछ भी गलत नहीं है Supplier। लेकिन जब आपको इसकी आवश्यकता नहीं है, तो इसे न बनाएं। जब आपको केवल एक की आवश्यकता होती है Supplier<String>, तो यह पहले से ही उपयोग करने के लिए पर्याप्त है DeclaringClass::generateTaskIdऔर एक स्पष्ट वर्ग की आवश्यकता को समाप्त करना इस भाषा की विशेषता है।
होल्गर

ईमानदार होने के लिए, मैं एक सिफारिश के औचित्य की ओर देख रहा था जिस पर मैं गुजर रहा था। काम पर किसी कारण से मुझे वास्तव में यह महसूस नहीं हुआ कि TaskIdSupplierकार्यान्वयन के प्रयास के लायक है, लेकिन फिर ServiceLoaderमेरे दिमाग से पूरी तरह से अवधारणा समाप्त हो गई। इन चर्चाओं हम जैसे कर रहे थे के दौरान कुछ सवाल का सामना करना पड़ा क्या का उपयोग है Supplierके publicअस्तित्व जब एक आगे जाना है और अपने स्वयं के इंटरफेस विकसित कर सकते हैं? और वैश्विक स्थिरांक के रूप में क्यों नहीं public static Supplier<String> TASK_ID_SUPPLIER = () ->...? । (१/२)
नमन

1
जावा में कार्यों का प्रतिनिधित्व करने के लिए @ अज्ञात तरीका तरीके हैं और उन कार्यों का मूल्यांकन करना उन्हें लागू करने के समान है। कभी भी किसी डेवलपर को इसके variable.genericMethodName(args)बजाय मजबूर नहीं होना चाहिए meaningfulMethodName(args)। किसी फ़ंक्शन का प्रतिनिधित्व करने के लिए एक वर्ग प्रकार का उपयोग करना, चाहे लैम्ब्डा अभिव्यक्ति / विधि संदर्भ या मैन्युअल रूप से बनाई गई क्लास के माध्यम से, फ़ंक्शन को पास करने के लिए केवल एक वाहन है (जावा में सच्चे फ़ंक्शन प्रकारों की अनुपस्थिति में)। यह केवल जरूरत पड़ने पर ही किया जाना चाहिए।
होल्गर

1
जब आपके पास एक छोटा सा कोड टुकड़ा होता है, जो केवल चारों ओर से गुजरता है, तो आप इसे लंबवत अभिव्यक्ति बना सकते हैं। जब भी इसे एक विधि की तरह आह्वान करने की आवश्यकता होती है (इसमें परीक्षण की आवश्यकता के साथ परिदृश्य शामिल होते हैं, जब कोड टुकड़ा तुच्छ नहीं होता है), एक नामांकित विधि बनाएं जिसे आमंत्रित किया जा सकता है और एक विधि संदर्भ या लैम्ब्डा अभिव्यक्ति या स्पष्ट वर्ग का उपयोग कर सकता है। एक कॉल encapsulating, जब जरूरत के आसपास इसे पारित करने के लिए। जब आप अपने कोड में एम्बेडेड लैम्ब्डा एक्सप्रेशन या विधि संदर्भों की दक्षता पर भरोसा नहीं करते हैं, तो दूसरे शब्दों में, उन्हें केवल तभी उपयोगी माना जाता है, जब उन्हें कभी जरूरत न हो।
होल्गर

12

जैसा कि दूसरों ने कहा है, एक कार्यात्मक इंटरफ़ेस एक इंटरफ़ेस है जो एक विधि को उजागर करता है। इसकी एक से अधिक विधियां हो सकती हैं, लेकिन अन्य सभी का डिफ़ॉल्ट कार्यान्वयन होना चाहिए। इसे "कार्यात्मक इंटरफ़ेस" कहा जाता है, क्योंकि यह प्रभावी रूप से एक फ़ंक्शन के रूप में कार्य करता है। चूंकि आप इंटरफेस को मापदंडों के रूप में पारित कर सकते हैं, इसका मतलब है कि फ़ंक्शन अब "प्रथम श्रेणी के नागरिक" हैं जैसे कि कार्यात्मक प्रोग्रामिंग भाषाओं में। इसके कई लाभ हैं, और आप स्ट्रीम एपीआई का उपयोग करते समय उन्हें काफी देखेंगे। बेशक, लैम्ब्डा अभिव्यक्ति उनके लिए मुख्य स्पष्ट उपयोग है।


10

हर्गिज नहीं। लैम्ब्डा एक्सप्रेशन उस एनोटेशन का एक और एकमात्र बिंदु हैं।


6
खैर, लैम्ब्डा एनोटेशन के बिना भी काम करते हैं। यह एक @Overrideसंकलक है जैसे संकलक को यह बताना है कि आपने कुछ ऐसा लिखने का इरादा किया है जो "कार्यात्मक" था (और यदि आप फिसल गए हैं तो एक त्रुटि प्राप्त करें)।
थिलो

1
सीधे बिंदु और सही उत्तर के लिए, हालांकि थोड़ा छोटा है। मैंने एक अधिक विस्तृत उत्तर जोड़ने के लिए समय लिया और अधिक शब्दों के साथ एक ही बात कह रहा हूँ ...
Holger

5

एक लैम्ब्डा अभिव्यक्ति को एक कार्यात्मक इंटरफ़ेस प्रकार को सौंपा जा सकता है, लेकिन इसलिए संदर्भ और अनाम कक्षाओं को विधि दे सकता है।

विशिष्ट कार्यात्मक इंटरफेस के बारे में एक अच्छी बात यह java.util.functionहै कि वे नए कार्यों (जैसे Function.andThenऔर , आदि) को बनाने के लिए रचे जा सकते हैं Function.compose, Predicate.andक्योंकि वे आसान डिफ़ॉल्ट तरीकों के कारण होते हैं।


आपको इस टिप्पणी के बारे में और विस्तार से बताना चाहिए। विधि संदर्भ और नए कार्यों के बारे में क्या?
निकोलस

5

केवल एक अमूर्त विधि वाले इंटरफेस को कार्यात्मक इंटरफ़ेस कहा जाता है। @FunctionalInterface का उपयोग करना अनिवार्य नहीं है, लेकिन गलती से अतिरिक्त रूप से जोड़ने से बचने के लिए कार्यात्मक इंटरफेस के साथ इसका उपयोग करना सबसे अच्छा अभ्यास है। यदि इंटरफ़ेस @FunctionalInterface एनोटेशन के साथ एनोटेट किया गया है और हम एक से अधिक अमूर्त पद्धति के लिए प्रयास करते हैं, तो यह संकलक त्रुटि फेंकता है।

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }

3

कार्यात्मक इंटरफ़ेस:

  • जावा 8 में पेश किया गया
  • इंटरफ़ेस जिसमें "एकल सार" विधि शामिल है।

उदाहरण 1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

उदाहरण 2:

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

उदाहरण 3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Java8 एनोटेशन - @FunctionalInterface

  • एनोटेशन की जाँच करें कि इंटरफ़ेस में केवल एक सार विधि है। यदि नहीं, तो त्रुटि बढ़ाएँ।
  • भले ही @FunctionalInterface गायब है, यह अभी भी कार्यात्मक इंटरफ़ेस है (यदि एकल सार विधि हो)। एनोटेशन गलतियों से बचने में मदद करता है।
  • कार्यात्मक इंटरफ़ेस में अतिरिक्त स्थिर और डिफ़ॉल्ट विधियाँ हो सकती हैं।
  • उदाहरण Iterable <>, तुलनीय <>, तुलनित्र <>।

कार्यात्मक इंटरफ़ेस के अनुप्रयोग:

  • विधि संदर्भ
  • लैम्ब्डा एक्सप्रेशन
  • निर्माता संदर्भ

कार्यात्मक इंटरफेस सीखने के लिए, इंटरफ़ेस में पहले डिफ़ॉल्ट तरीके सीखें, और कार्यात्मक इंटरफ़ेस सीखने के बाद, विधि संदर्भ और लैम्ब्डा अभिव्यक्ति को समझना आपके लिए आसान होगा


क्या आपके पहले दो उदाहरणों में 'अमूर्त' कीवर्ड होना चाहिए?
sofs1

1
@ sofs1 इंटरफेस में घोषित तरीके सार्वजनिक और अमूर्त दोनों तरह से होते हैं। अमूर्त वर्ग में विधियों के मामले में आपको अमूर्त खोजशब्द का उपयोग करना होगा। हालाँकि इंटरफ़ेस में विधियों के लिए अमूर्त खोजशब्द का उपयोग करना ठीक है। उन्होंने इसे पुराने जावा संस्करण की अनुकूलता के लिए अनुमति दी है, लेकिन इसे हतोत्साहित किया गया है।
केतन

2

आप जावा 8 में लैम्ब्डा का उपयोग कर सकते हैं

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

के बारे में अधिक जानकारी के लिए जावा lambdas और FunctionalInterfaces


1

@FunctionalInterface जावा 8 के साथ एक नया एनोटेशन जारी किया जाता है और लैम्ब्डा एक्सप्रेशन के लिए लक्ष्य प्रकार प्रदान करता है और यह आपके कोड के संकलन समय की जाँच में उपयोग किया जाता है।

जब आप इसका उपयोग करना चाहते हैं:

1- आपके इंटरफ़ेस में एक से अधिक सार विधियाँ नहीं होनी चाहिए , अन्यथा संकलन त्रुटि दी जाएगी।

1- आपका इंटरफ़ेस शुद्ध होना चाहिए, जिसका अर्थ है कि कार्यात्मक इंटरफ़ेस को स्टेटलेस कक्षाओं द्वारा कार्यान्वित करने का इरादा है, शुद्ध का बहिष्कार Comparatorइंटरफ़ेस है, क्योंकि यह कार्यान्वयनकर्ताओं की स्थिति पर निर्भर नहीं है, इस मामले में कोई संकलन त्रुटि नहीं दी जाएगी, लेकिन कई मामलों में आपको इस तरह के इंटरफेस के साथ लैम्ब्डा का उपयोग नहीं कर पाएंगे

java.util.functionपैकेज विभिन्न सामान्य प्रयोजन कार्यात्मक इंटरफेस जैसे होता है Predicate, Consumer, Function, और Supplier

कृपया ध्यान दें कि आप इस एनोटेशन के बिना लैम्ब्डा का उपयोग कर सकते हैं।


1

अन्य उत्तरों के अलावा, मुझे लगता है कि "लैम्बडा एक्सप्रेशंस के साथ सीधे कार्यात्मक इंटरफ़ेस का उपयोग करने का मुख्य कारण" जावा भाषा की प्रकृति से संबंधित हो सकता है जो ऑब्जेक्ट ओरिएंटेड है।

लैम्ब्डा के भावों की मुख्य विशेषताएं हैं: 1. उन्हें लगभग 2. पास किया जा सकता है और वे विशिष्ट समय (कई बार) में भविष्य में निष्पादित कर सकते हैं। अब भाषाओं में इस सुविधा का समर्थन करने के लिए, कुछ अन्य भाषाएँ बस इस मामले से निपटती हैं।

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

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

या ES6 के माध्यम से, आप एक तीर फ़ंक्शन का उपयोग कर सकते हैं।

const myFunction = ... => ...

अब तक, जावा भाषा डिजाइनरों ने इन तरीकों (कार्यात्मक प्रोग्रामिंग तकनीकों) के माध्यम से उल्लिखित सुविधाओं को संभालने के लिए स्वीकार नहीं किया है। उनका मानना ​​है कि जावा भाषा ऑब्जेक्ट ओरिएंटेड है और इसलिए उन्हें ऑब्जेक्ट ओरिएंटेड तकनीकों के माध्यम से इस समस्या को हल करना चाहिए। वे सरलता और जावा भाषा की निरंतरता को याद नहीं करना चाहते हैं।

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

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