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
इंटरफ़ेस को लागू करते हैं।
उदाहरण:
कोड | टिप्पणी |
---|---|
|
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();
}
}
स्ट्रिंग की लंबाई की तुलना करने के लिए, बस एक लंबाई को दूसरी से घटाएं।
एक प्रोग्राम के लिए पूरा कोड जो स्ट्रिंग्स को लंबाई से सॉर्ट करता है वह इस तरह दिखेगा:
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();
}
}
दो अलग-अलग मामलों में समान कोड ब्लॉक को इंगित करने के लिए एक ही रंग का उपयोग किया जाता है। मतभेद व्यवहार में काफी छोटे हैं।
जब कंपाइलर कोड के पहले ब्लॉक का सामना करता है, तो यह कोड के संबंधित दूसरे ब्लॉक को उत्पन्न करता है और कक्षा को कुछ यादृच्छिक नाम देता है।
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)
GO TO FULL VERSION