1. इंटरफेस

यह समझने के लिए कि लैम्ब्डा फ़ंक्शंस क्या हैं, आपको पहले यह समझने की ज़रूरत है कि इंटरफेस क्या हैं। तो, आइए मुख्य बिंदुओं को याद करें।

एक इंटरफ़ेस एक वर्ग की अवधारणा का रूपांतर है। एक भारी छंटनी वाला वर्ग, मान लीजिए। एक वर्ग के विपरीत, एक इंटरफ़ेस के अपने स्वयं के चर नहीं हो सकते (स्थिर वाले को छोड़कर)। आप ऐसी वस्तुएँ भी नहीं बना सकते जिनका प्रकार एक इंटरफ़ेस है:

  • आप वर्ग के चर घोषित नहीं कर सकते
  • आप ऑब्जेक्ट नहीं बना सकते

उदाहरण:

interface Runnable
{
   void run();
}
एक मानक इंटरफ़ेस का उदाहरण

एक इंटरफ़ेस का उपयोग करना

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

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

interface Runnable
{
   void run();
}

class Timer implements Runnable
{
   void run()
   {
      System.out.println(LocalTime.now());
   }
}

class Calendar implements Runnable
{
   void run()
   {
      var date = LocalDate.now();
      System.out.println("Today: " + date.getDayOfWeek());
   }
}

वर्ग इंटरफ़ेस को Timerलागू करता है Runnable, इसलिए इसे अपने अंदर उन सभी विधियों की घोषणा करनी चाहिए जो Runnableइंटरफ़ेस में हैं और उन्हें लागू करें, अर्थात एक विधि निकाय में कोड लिखें। वही Calendarकक्षा के लिए जाता है।

लेकिन अब Runnableचर उन वस्तुओं के संदर्भों को संग्रहीत कर सकते हैं जो Runnableइंटरफ़ेस को लागू करते हैं।

उदाहरण:

कोड टिप्पणी
Timer timer = new Timer();
timer.run();

Runnable r1 = new Timer();
r1.run();

Runnable r2 = new Calendar();
r2.run();

run()क्लास में मेथड को कॉल Timerकिया जाएगा क्लास में मेथड को


कॉल किया जाएगा क्लास में मेथड को कॉल किया जाएगा run()Timer


run()Calendar

आप हमेशा किसी भी प्रकार के चर के लिए एक वस्तु संदर्भ निर्दिष्ट कर सकते हैं, जब तक कि वह प्रकार वस्तु के पूर्वज वर्गों में से एक है। Timerऔर वर्गों के लिए Calendar, ऐसे दो प्रकार हैं: Objectऔर Runnable

यदि आप एक चर के लिए एक वस्तु संदर्भ निर्दिष्ट करते हैं , तो आप केवल कक्षा Objectमें घोषित विधियों को ही कॉल कर सकते हैं । Objectऔर यदि आप एक चर के लिए एक वस्तु संदर्भ निर्दिष्ट करते हैं , तो आप प्रकार Runnableके तरीकों को कॉल कर सकते हैं ।Runnable

उदाहरण 2:

ArrayList<Runnable> list = new ArrayList<Runnable>();
list.add (new Timer());
list.add (new Calendar());

for (Runnable element: list)
    element.run();

यह कोड काम करेगा, क्योंकि Timerऔर Calendarऑब्जेक्ट्स में ऐसे तरीके हैं जो पूरी तरह से अच्छी तरह से काम करते हैं। इसलिए उन्हें कॉल करना कोई समस्या नहीं है। यदि हमने दोनों वर्गों में अभी एक रन () विधि जोड़ी है, तो हम उन्हें इतने सरल तरीके से कॉल नहीं कर पाएंगे।

मूल रूप से, Runnableइंटरफ़ेस का उपयोग केवल रन विधि डालने के स्थान के रूप में किया जाता है।



2. छँटाई

आइए कुछ और व्यावहारिक पर चलते हैं। उदाहरण के लिए, आइए स्ट्रिंग्स को सॉर्ट करते हुए देखें।

स्ट्रिंग्स के संग्रह को वर्णानुक्रम में क्रमबद्ध करने के लिए, जावा में एक शानदार विधि हैCollections.sort(collection);

यह स्थैतिक विधि उत्तीर्ण संग्रह को क्रमबद्ध करती है। और छँटाई की प्रक्रिया में, यह समझने के लिए कि क्या तत्वों की अदला-बदली की जानी चाहिए, यह अपने तत्वों की जोड़ीदार तुलना करता है।

छँटाई के दौरान, इन तुलनाओं को compareTo() विधि का उपयोग करके किया जाता है, जिसमें सभी मानक वर्ग हैं: Integer, String, ...

पूर्णांक वर्ग की तुलना करने के लिए () विधि दो संख्याओं के मूल्यों की तुलना करती है, जबकि स्ट्रिंग वर्ग की तुलना () विधि तारों के वर्णमाला क्रम को देखती है।

तो संख्याओं का संग्रह आरोही क्रम में क्रमबद्ध किया जाएगा, जबकि तारों का संग्रह वर्णानुक्रम में क्रमबद्ध किया जाएगा।

वैकल्पिक छँटाई एल्गोरिदम

लेकिन क्या होगा अगर हम तार को वर्णानुक्रम में नहीं, बल्कि उनकी लंबाई के अनुसार क्रमबद्ध करना चाहते हैं? और क्या होगा यदि हम संख्याओं को अवरोही क्रम में क्रमबद्ध करना चाहते हैं? आप इस मामले में क्या करते हैं?

ऐसी स्थितियों को संभालने के लिए, Collectionsवर्ग के पास एक और sort()तरीका है जिसमें दो पैरामीटर हैं:

Collections.sort(collection, comparator);

जहां तुलनित्र एक विशेष वस्तु है जो सॉर्ट ऑपरेशन के दौरान संग्रह में वस्तुओं की तुलना करना जानता है। तुलनित्र शब्द अंग्रेजी शब्द तुलनित्र से आया है , जो बदले में तुलना से निकला है , जिसका अर्थ है "तुलना करना"।

तो यह विशेष वस्तु क्या है?

Comparatorइंटरफेस

अच्छा, यह सब बहुत आसान है। sort()विधि के दूसरे पैरामीटर का प्रकार हैComparator<T>

जहाँ T एक प्रकार का पैरामीटर है जो संग्रह में तत्वों के प्रकार को इंगित करता है , और Comparatorएक इंटरफ़ेस है जिसमें एक ही विधि हैint compare(T obj1, T obj2);

दूसरे शब्दों में, एक तुलनित्र वस्तु किसी भी वर्ग की कोई वस्तु है जो तुलनित्र इंटरफ़ेस को लागू करती है। तुलनित्र इंटरफ़ेस बहुत सरल दिखता है:

public interface Comparator<Type>
{
   public int compare(Type obj1, Type obj2);
}
तुलनित्र इंटरफ़ेस के लिए कोड

विधि compare()उन दो तर्कों की तुलना करती है जो इसे पास किए गए हैं।

यदि विधि ऋणात्मक संख्या लौटाती है, तो इसका अर्थ है obj1 < obj2। यदि विधि सकारात्मक संख्या लौटाती है, तो इसका मतलब है obj1 > obj2। यदि विधि 0 लौटाती है, तो इसका मतलब है obj1 == obj2

यहाँ एक तुलनित्र वस्तु का एक उदाहरण दिया गया है जो तार की लंबाई से तुलना करता है:

public class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
}
StringLengthComparatorकक्षा का कोड

स्ट्रिंग की लंबाई की तुलना करने के लिए, बस एक लंबाई को दूसरी से घटाएं।

एक प्रोग्राम के लिए पूरा कोड जो स्ट्रिंग्स को लंबाई से सॉर्ट करता है वह इस तरह दिखेगा:

public class Solution
{
   public static void main(String[] args)
   {
      ArrayList<String> list = new ArrayList<String>();
      Collections.addAll(list, "Hello", "how's", "life?");
      Collections.sort(list, new StringLengthComparator());
   }
}

class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
}
लंबाई के आधार पर तार छाँटना


3. सिंथेटिक शुगर

आपको क्या लगता है, क्या यह कोड अधिक संक्षिप्त रूप से लिखा जा सकता है? मूल रूप से, केवल एक पंक्ति है जिसमें उपयोगी जानकारी है - obj1.length() - obj2.length();.

लेकिन कोड एक विधि के बाहर मौजूद नहीं हो सकता है, इसलिए हमें एक विधि जोड़नी पड़ी compare(), और विधि को संग्रहीत करने के लिए हमें एक नई कक्षा - जोड़नी पड़ी StringLengthComparator। और हमें वेरिएबल्स के प्रकारों को निर्दिष्ट करने की भी आवश्यकता है... सब कुछ सही प्रतीत होता है।

लेकिन इस कोड को छोटा करने के तरीके हैं। हमारे पास आपके लिए कुछ सिंटैक्टिक शुगर है। दो स्कूप!

अनाम आंतरिक वर्ग

आप विधि के अंदर तुलनित्र कोड लिख सकते हैं main(), और संकलक बाकी काम करेगा। उदाहरण:

public class Solution
{
    public static void main(String[] args)
    {
        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list, "Hello", "how's", "life?");

        Comparator<String> comparator = new Comparator<String>()
        {
            public int compare (String obj1, String obj2)
            {
                return obj1.length() – obj2.length();
            }
        };

        Collections.sort(list, comparator);
    }
}
स्ट्रिंग्स को लंबाई के अनुसार क्रमबद्ध करें

आप एक ऐसी वस्तु बना सकते हैं जो Comparatorस्पष्ट रूप से एक वर्ग बनाए बिना इंटरफ़ेस को लागू करती है! कंपाइलर इसे स्वचालित रूप से बनाएगा और इसे कुछ अस्थायी नाम देगा। आइए तुलना करें:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
};
अनाम आंतरिक वर्ग
Comparator<String> comparator = new StringLengthComparator();

class StringLengthComparator implements Comparator<String>
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
}
StringLengthComparatorकक्षा

दो अलग-अलग मामलों में समान कोड ब्लॉक को इंगित करने के लिए एक ही रंग का उपयोग किया जाता है। मतभेद व्यवहार में काफी छोटे हैं।

जब कंपाइलर कोड के पहले ब्लॉक का सामना करता है, तो यह कोड के संबंधित दूसरे ब्लॉक को उत्पन्न करता है और कक्षा को कुछ यादृच्छिक नाम देता है।


4. जावा में लैम्ब्डा भाव

मान लीजिए कि आप अपने कोड में अज्ञात आंतरिक कक्षा का उपयोग करने का निर्णय लेते हैं। इस मामले में, आपके पास कोड का एक ब्लॉक इस तरह होगा:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
};
अनाम आंतरिक वर्ग

यहां हम एक अज्ञात वर्ग के निर्माण के साथ एक चर की घोषणा को जोड़ते हैं। लेकिन इस कोड को छोटा करने का एक तरीका है। उदाहरण के लिए, इस तरह:

Comparator<String> comparator = (String obj1, String obj2) ->
{
    return obj1.length() – obj2.length();
};

अर्धविराम की आवश्यकता है क्योंकि यहां न केवल एक निहित वर्ग घोषणा है, बल्कि एक चर का निर्माण भी है।

इस तरह के नोटेशन को लैम्ब्डा एक्सप्रेशन कहा जाता है।

यदि कंपाइलर आपके कोड में इस तरह के नोटेशन का सामना करता है, तो यह कोड के वर्बोज़ संस्करण (एक अज्ञात आंतरिक वर्ग के साथ) उत्पन्न करता है।

ध्यान दें कि लैम्ब्डा एक्सप्रेशन लिखते समय, हमने न केवल क्लास का नाम छोड़ा , बल्कि मेथड का नाम भी छोड़ा।Comparator<String>int compare()

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

आइए कोड के वर्बोज़ संस्करण को फिर से देखें, लेकिन हम उस हिस्से को ग्रे कर देंगे जिसे लैम्ब्डा एक्सप्रेशन लिखते समय छोड़ा जा सकता है:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
};
अनाम आंतरिक वर्ग

ऐसा लगता है कि कुछ भी महत्वपूर्ण नहीं छोड़ा गया था। वास्तव में, यदि Comparatorइंटरफ़ेस में केवल एक ही compare()विधि है, तो संकलक शेष कोड से ग्रे-आउट कोड को पूरी तरह से पुनर्प्राप्त कर सकता है।

छंटाई

वैसे, अब हम इस तरह छँटाई कोड लिख सकते हैं:

Comparator<String> comparator = (String obj1, String obj2) ->
{
   return obj1.length() – obj2.length();
};
Collections.sort(list, comparator);

या इस तरह भी:

Collections.sort(list, (String obj1, String obj2) ->
   {
      return obj1.length() – obj2.length();
   }
);

हमने बस comparatorवेरिएबल को तुरंत उस मान से बदल दिया जो comparatorवेरिएबल को सौंपा गया था।

अनुमान टाइप करें

लेकिन वह सब नहीं है। इन उदाहरणों में कोड को और भी संक्षिप्त रूप से लिखा जा सकता है। सबसे पहले, संकलक अपने लिए यह निर्धारित कर सकता है कि obj1और obj2चर हैं Strings। और दूसरा, कर्ली ब्रेसिज़ और रिटर्न स्टेटमेंट को भी छोड़ा जा सकता है यदि आपके पास मेथड कोड में केवल एक ही कमांड है।

संक्षिप्त संस्करण इस प्रकार होगा:

Comparator<String> comparator = (obj1, obj2) ->
   obj1.length() – obj2.length();

Collections.sort(list, comparator);

और यदि वेरिएबल का उपयोग करने के बजाय comparator, हम तुरंत इसके मान का उपयोग करते हैं, तो हमें निम्न संस्करण मिलता है:

Collections.sort(list, (obj1, obj2) ->  obj1.length() — obj2.length() );

खैर, आप इसके बारे में क्या सोचते हैं? बिना किसी अनावश्यक जानकारी के कोड की केवल एक पंक्ति - केवल चर और कोड। इसे छोटा करने का कोई तरीका नहीं है! या है?



5. यह कैसे काम करता है

वास्तव में, कोड को और भी अधिक संक्षिप्त रूप से लिखा जा सकता है। लेकिन उस पर बाद में।

आप एक लैम्ब्डा एक्सप्रेशन लिख सकते हैं जहाँ आप एक एकल विधि के साथ एक इंटरफ़ेस प्रकार का उपयोग करेंगे।

उदाहरण के लिए, कोड में , आप लैम्ब्डा अभिव्यक्ति लिख सकते हैं क्योंकि विधि का हस्ताक्षर इस प्रकार है:Collections.sort(list, (obj1, obj2) -> obj1.length() - obj2.length());sort()

sort(Collection<T> colls, Comparator<T> comp)

जब हमने ArrayList<String>संग्रह को सॉर्ट विधि के पहले तर्क के रूप में पारित किया, तो संकलक यह निर्धारित करने में सक्षम था कि दूसरे तर्क का प्रकार है । और इससे यह निष्कर्ष निकला कि इस इंटरफ़ेस का एक ही तरीका है। बाकी सब कुछ एक तकनीकी है।Comparator<String>int compare(String obj1, String obj2)