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.

उदाहरण २:

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

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

हा कोड कार्य करेल, कारण Timerआणि Calendarऑब्जेक्ट्समध्ये उत्तम प्रकारे कार्य करणाऱ्या पद्धती आहेत. म्हणून, त्यांना कॉल करणे ही समस्या नाही. जर आम्ही दोन्ही क्लासेसमध्ये रन() पद्धत जोडली असती, तर आम्ही त्यांना इतक्या सोप्या पद्धतीने कॉल करू शकणार नाही.

मूलभूतपणे, Runnableइंटरफेस फक्त रन पद्धत ठेवण्यासाठी एक जागा म्हणून वापरला जातो.



2. वर्गीकरण

चला अधिक व्यावहारिक गोष्टीकडे वळूया. उदाहरणार्थ, स्ट्रिंग्सचे वर्गीकरण पाहू.

वर्णक्रमानुसार स्ट्रिंग्सच्या संग्रहाची क्रमवारी लावण्यासाठी, Java मध्ये एक उत्तम पद्धत आहे ज्याला म्हणतातCollections.sort(collection);

ही स्थिर पद्धत पास केलेल्या संग्रहाची क्रमवारी लावते. आणि वर्गीकरणाच्या प्रक्रियेत, घटकांची अदलाबदल करावी की नाही हे समजून घेण्यासाठी ते घटकांची जोडीने तुलना करते.

वर्गीकरणादरम्यान, या तुलना compareTo() पद्धती वापरून केल्या जातात, ज्या सर्व मानक वर्गांमध्ये आहेत: Integer, String, ...

पूर्णांक वर्गाची compareTo() पद्धत दोन संख्यांच्या मूल्यांची तुलना करते, तर स्ट्रिंग वर्गाची compareTo() पद्धत स्ट्रिंगच्या वर्णक्रमानुसार पाहते.

त्यामुळे संख्यांचा संग्रह चढत्या क्रमाने लावला जाईल, तर स्ट्रिंगचा संग्रह वर्णक्रमानुसार लावला जाईल.

पर्यायी क्रमवारी अल्गोरिदम

पण जर आपल्याला स्ट्रिंग्सची क्रमवारी वर्णानुक्रमाने नव्हे तर लांबीनुसार करायची असेल तर? आणि जर आपल्याला संख्या उतरत्या क्रमाने लावायची असेल तर? या प्रकरणात तुम्ही काय करता?

अशा परिस्थिती हाताळण्यासाठी, Collectionsवर्गात आणखी एक पद्धत आहे sort()ज्यामध्ये दोन पॅरामीटर्स आहेत:

Collections.sort(collection, comparator);

जेथे तुलनाकर्ता एक विशेष ऑब्जेक्ट आहे ज्याला क्रमवारी ऑपरेशन दरम्यान संग्रहातील वस्तूंची तुलना कशी करावी हे माहित असते . comparator हा शब्द इंग्लिश शब्द comparator वरून आला आहे, जो compare वरून आला आहे , म्हणजे "तुलना करणे".

मग ही विशेष वस्तू काय आहे?

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. जावा मध्ये Lambda अभिव्यक्ती

समजा तुम्ही तुमच्या कोडमध्ये निनावी अंतर्गत वर्ग वापरण्याचे ठरवले आहे. या प्रकरणात, आपल्याकडे याप्रमाणे कोडचा एक ब्लॉक असेल:

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();
};

अर्धविराम आवश्यक आहे कारण येथे आपल्याकडे केवळ अंतर्निहित वर्ग घोषणाच नाही तर व्हेरिएबलची निर्मिती देखील आहे.

अशा नोटेशनला लॅम्बडा अभिव्यक्ती म्हणतात .

जर कंपाइलरला तुमच्या कोडमध्ये असे नोटेशन आढळले तर ते फक्त कोडची व्हर्बोज आवृत्ती तयार करते (निनावी अंतर्गत वर्गासह).

लक्षात घ्या की lambda अभिव्यक्ती लिहिताना, आम्ही केवळ वर्गाचे नावच नाही तर पद्धतीचे नाव देखील वगळले आहे .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)