जावा 8 लैंबडास बनाम बेनामी कक्षाएं


111

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

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

Collections.sort(personList, new Comparator<Person>(){
  public int compare(Person p1, Person p2){
    return p1.firstName.compareTo(p2.firstName);
  }
});

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

Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));

और आश्चर्यजनक रूप से संक्षिप्त दिखता है। तो मेरा सवाल यह है कि क्या लैम्बडास के बजाय जावा में उन कक्षाओं का उपयोग करने का कोई कारण है?

संपादित करें

एक ही सवाल लेकिन विपरीत दिशा में, अनाम कक्षाओं के बजाय लैम्ब्डा का उपयोग करने के क्या फायदे हैं, क्योंकि लैम्ब्डा का उपयोग केवल एकल विधि इंटरफेस के साथ किया जा सकता है, क्या यह नई सुविधा केवल कुछ मामलों में उपयोग किया जाने वाला शॉर्टकट है या यह वास्तव में उपयोगी है?


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

11
बस आपकी जानकारी के लिए, आप तुलनित्र का निर्माण इस प्रकार कर सकते हैं: Comparator.comparing(Person::getFirstName)यदि getFirstName()कोई विधि वापस आ जाएगी firstName
स्किवि

1
या कई विधियों के साथ अनाम कक्षाएं, या ...
मार्क रोटेटेवेल

1
मैं विशेष रूप से के बाद अतिरिक्त प्रश्न के कारण बहुत व्यापक रूप पास के लिए वोट करने के लिए परीक्षा रहा हूँ, संपादित करें
मार्क रोटेवेल

1
इस विषय पर एक अच्छा-इन-डेप्थ लेख: infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
Ram Patra

जवाबों:


108

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

मुझे लगता है कि एआईसी के अधिकांश उपयोग एकल कार्यों के सांविधिक कार्यान्वयन प्रदान करने के लिए थे और इसलिए इसे लैम्ब्डा अभिव्यक्तियों के साथ बदला जा सकता है, लेकिन एआईसी के अन्य उपयोग भी हैं जिनके लिए लैम्ब्डा का उपयोग नहीं किया जा सकता है। ठहरने के लिए यहां ए.आई.सी.

अपडेट करें

AIC और लैम्ब्डा एक्सप्रेशन के बीच एक और अंतर यह है कि AIC एक नया स्कोप पेश करता है। यही है, नामों को एआईसी के सुपरक्लास और इंटरफेस से हल किया जाता है और वे नाम छायांकित कर सकते हैं जो पर्यावरणीय रूप से घेरने वाले वातावरण में होते हैं। लैम्ब्डा के लिए, सभी नामों को शाब्दिक रूप से हल किया जाता है।


1
लम्बदास का राज्य हो सकता है। इस संबंध में, मुझे लैम्ब्डा और एआईसी के बीच कोई अंतर नहीं दिखता है।
nosid

1
@ एनोसिड एआईसी, किसी भी वर्ग के उदाहरणों की तरह, खेतों में स्थिति को पकड़ सकता है, और यह राज्य कक्षा की किसी भी विधि के लिए (और संभावित रूप से परस्पर) सुलभ है। यह स्थिति तब तक मौजूद रहती है जब तक कि ऑब्जेक्ट GC'd नहीं है, अर्थात, इसकी अनिश्चित सीमा है, इसलिए यह विधि कॉल पर बनी रह सकती है। जिस समय लंबोदर का सामना किया जाता है उस समय अनिश्चित काल के लंबोदर को पकड़ लिया जाता है; यह राज्य अपरिवर्तनीय है। एक लैम्ब्डा के भीतर स्थानीय चर परस्पर भिन्न होते हैं, लेकिन वे केवल तभी मौजूद होते हैं जब एक लैम्ब्डा आह्वान जारी होता है।
स्टुअर्ट मार्क्स

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

2
AIC कुछ इस तरह से एक फ़ाइल बनाएगा, A$1.classलेकिन लैम्ब्डा नहीं करेगा। क्या मैं इसे अंतर में जोड़ सकता हूं?
आसिफ मुश्ताक

2
@UnKnown यह मुख्य रूप से एक कार्यान्वयन चिंता है; यह इस बात को प्रभावित नहीं करता है कि एआईसीएस बनाम लैम्ब्डा के साथ एक कार्यक्रम कैसे होता है, यही सवाल ज्यादातर के बारे में है। ध्यान दें कि एक लैम्ब्डा अभिव्यक्ति एक वर्ग को उत्पन्न करता है जैसे नाम LambdaClass$$Lambda$1/1078694789। हालाँकि, यह वर्ग लैम्बडा मेटाफैक्शन द्वारा ऑन-द-फ्लाई जेनरेट किया जाता है javac, द्वारा नहीं , इसलिए कोई संगत .classफ़ाइल नहीं है। फिर भी, यह एक कार्यान्वयन चिंता है।
स्टुअर्ट मार्क्स

60

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

इसलिए, नहीं, हम केवल अनाम कक्षाओं को अनदेखा नहीं कर सकते। और बस FYI करें, अपने sort()विधि अधिक सरल किया जा सकता, के लिए प्रकार घोषणा लंघन द्वारा p1और p2:

Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));

आप यहां विधि संदर्भ का भी उपयोग कर सकते हैं। या तो आप कक्षा compareByFirstName()में एक विधि जोड़ते हैं Person, और उपयोग करते हैं:

Collections.sort(personList, Person::compareByFirstName);

या, firstNameसीधे विधि Comparatorसे प्राप्त करने के लिए एक गेटर जोड़ें Comparator.comparing():

Collections.sort(personList, Comparator.comparing(Person::getFirstName));

4
मुझे पता था, लेकिन मैं पठनीयता के मामले में सबसे लंबे समय तक पसंद करता हूं, क्योंकि अन्यथा यह पता लगाने में भ्रमित हो सकता है कि ये चर कहां से आते हैं।
अमीन अबू-तालेब

@ AminAbu- तालेब यह भ्रामक क्यों होगा। यह एक मान्य लंबोदर सिंटैक्स है। प्रकार वैसे भी अनुमान है। वैसे भी, यह एक व्यक्तिगत पसंद है। आप स्पष्ट प्रकार दे सकते हैं। कोई बात नहीं।
रोहित जैन

1
लैम्ब्डा और अनाम कक्षाओं के बीच एक और सूक्ष्म अंतर है: बेनामी कक्षाएं सीधे नए जावा 8 प्रकार एनोटेशन के साथ एनोटेट की जा सकती हैं , जैसे new @MyTypeAnnotation SomeInterface(){};। लंबोदर भाव के लिए यह संभव नहीं है। विवरण के लिए, मेरा प्रश्न यहां देखें: एक लैम्ब्डा एक्सप्रेशन के कार्यात्मक इंटरफ़ेस की व्याख्या
बाल्ड

36

अनाम कक्षाओं के साथ लैम्ब्डा प्रदर्शन

जब एप्लिकेशन लॉन्च किया जाता है, तो प्रत्येक कक्षा की फ़ाइल लोड और सत्यापित होनी चाहिए।

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

लेम्बोडा बायटेकोड पीढ़ी में भिन्न हैं, वे अधिक कुशल हैं, इनवॉकेन्डेमिक निर्देश का उपयोग किया जाता है जो JDK7 के साथ आता है।

लैंबडास के लिए इस निर्देश का उपयोग लैम्बडा अभिव्यक्ति को बाईटेकोड अनिल रनटाइम में देरी करने के लिए किया जाता है। (केवल पहली बार निर्देश दिया जाएगा)

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


प्रत्येक लैम्ब्डा को एक नए वर्ग की भी आवश्यकता होती है, लेकिन यह रनटाइम पर उत्पन्न होता है, इसलिए इस अर्थ में, लैम्ब्डा अनाम कक्षाओं की तुलना में अधिक कुशल नहीं है। अनाम श्रेणी के नए उदाहरण बनाने के लिए invokedynamicआम तौर पर लंबोदर बनाए जाते हैं invokespecial। तो, इस लिहाज से लंबोदर बहुत धीमे हैं (हालाँकि, JVM invokedynamicज्यादातर समय कॉल को ऑप्टिमाइज़ कर सकता है )।
झेकाकोजलोव

3
@AndreiTomashpolskiy 1. कृपया विनम्र रहें। 2. इस टिप्पणी को एक कंपाइलर इंजीनियर द्वारा पढ़ें: habrahabr.ru/post/313350/comments/#comment_9885460
ZhekaKozlov

@ZhekaKozlov, आपको JRE स्रोत कोड पढ़ने और javap / डीबगर का उपयोग करने के लिए एक कंपाइलर इंजीनियर होने की आवश्यकता नहीं है। आप जो याद कर रहे हैं, वह यह है कि लैम्ब्डा विधि के लिए एक रैपर क्लास की पीढ़ी पूरी तरह से मेमोरी में होती है और कुछ भी नहीं के बगल में खर्च होती है, जबकि एआईसी को तत्काल करने से संबंधित क्लास रिसोर्स को हल करना और लोड करना शामिल होता है (जिसका अर्थ है I / O सिस्टम कॉल)। इसलिए, invokedynamicसंकलित अनाम वर्गों की तुलना में तदर्थ वर्ग की पीढ़ी तेजी से धधक रही है।
आंद्रेई तमाशपोलस्की जू

@AndreiTomashpolskiy I / O जरूरी धीमा नहीं है
ZhekaKozlov

14

निम्नलिखित अंतर हैं:

1) सिंटेक्स

बेनामी इनर क्लास (AIC) की तुलना में लैम्बडा के भाव साफ-सुथरे लगते हैं

public static void main(String[] args) {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("in run");
        }
    };

    Thread t = new Thread(r);
    t.start(); 
}

//syntax of lambda expression 
public static void main(String[] args) {
    Runnable r = ()->{System.out.println("in run");};
    Thread t = new Thread(r);
    t.start();
}

2। घेरा

एक अनाम आंतरिक वर्ग एक वर्ग है, जिसका अर्थ है कि इसमें आंतरिक वर्ग के अंदर परिभाषित चर की गुंजाइश है।

जबकि, लैम्ब्डा अभिव्यक्ति स्वयं का एक दायरा नहीं है, बल्कि परिमार्जन के दायरे का हिस्सा है।

अनाम आंतरिक वर्ग और लैम्ब्डा अभिव्यक्ति का उपयोग करते समय इसी तरह का नियम सुपर और इस कीवर्ड के लिए लागू होता है । अनाम आंतरिक वर्ग के मामले में यह कीवर्ड स्थानीय क्षेत्र को संदर्भित करता है और सुपर कीवर्ड अनाम वर्ग के सुपर क्लास को संदर्भित करता है। जबकि लंबोदर अभिव्यक्ति के मामले में यह कीवर्ड संलग्न प्रकार के ऑब्जेक्ट को संदर्भित करता है और सुपर संलग्न वर्ग के सुपर क्लास को संदर्भित करेगा।

//AIC
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = new Runnable() {
            @Override
            public void run() {
                int cnt = 5;    
                System.out.println("in run" + cnt);
            }
        };

        Thread t = new Thread(r);
        t.start();
    }

//Lambda
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = ()->{
            int cnt = 5; //compilation error
            System.out.println("in run"+cnt);};
        Thread t = new Thread(r);
        t.start();
    }

3) प्रदर्शन

रनटाइम अनाम आंतरिक कक्षाओं में कक्षा लोडिंग, मेमोरी आवंटन और ऑब्जेक्ट इनिशियलाइज़ेशन और एक गैर-स्टैटिक विधि के मंगलाचरण की आवश्यकता होती है, जबकि लैम्ब्डा एक्सप्रेशन शुद्ध संकलन गतिविधि है और रनटाइम के दौरान अतिरिक्त लागत को लाइक नहीं करते हैं। अनाम आंतरिक कक्षाओं की तुलना में लैम्ब्डा अभिव्यक्ति का प्रदर्शन बेहतर है। **

** मुझे पता है कि यह बिंदु पूरी तरह से सच नहीं है। कृपया विवरण के लिए निम्नलिखित प्रश्न देखें। लैम्ब्डा बनाम अनाम आंतरिक वर्ग का प्रदर्शन: क्लासलोडर पर लोड को कम करना?


3

लैम्ब्डा के जावा 8 को कार्यात्मक प्रोग्रामिंग के लिए पेश किया गया था। जहां आप बॉयलरप्लेट कोड से बच सकते हैं। मुझे यह दिलचस्प लेख लैम्ब्डा पर आया।

http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html

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


0

अनाम वर्ग वहाँ रहने के लिए है क्योंकि लैम्बडा एकल सार विधियों के साथ कार्यों के लिए अच्छा है, लेकिन अन्य सभी मामलों के लिए अनाम आंतरिक वर्ग आपके उद्धारकर्ता हैं।


-1
  • लैम्ब्डा सिंटैक्स को स्पष्ट कोड लिखने की आवश्यकता नहीं है कि जावा का अनुमान लगाया जा सकता है।
  • उपयोग करके invoke dynamic, लैम्ब्डा को संकलन समय के दौरान अनाम कक्षाओं में वापस नहीं बदला जाता है (जावा को ऑब्जेक्ट बनाने के माध्यम से नहीं जाना पड़ता है, बस विधि के हस्ताक्षर के बारे में परवाह है, ऑब्जेक्ट बनाने के बिना विधि से बांध सकता है
  • लैम्ब्डा ने इस पर अधिक जोर दिया कि हम क्या करना चाहते हैं इसके बजाय हमें क्या करना है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.