हम लैंबडास का उपयोग करते हुए ऑब्जेक्ट गुणों के लिए स्थिर लिंक का उपयोग करते हैं

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


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


संपत्ति के नाम का उपयोग करें


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


person.contact.address.city 

इस विधि के साथ समस्या यह है कि इसके साथ नाम और संपत्ति के प्रकार की वर्तनी पर किसी नियंत्रण की कमी है:


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

नतीजतन, हम अभी भी एक स्थिर प्रकार-सुरक्षित संपत्ति संदर्भ रखना चाहते हैं। इस भूमिका के लिए एक सबसे अच्छा उम्मीदवार है, क्योंकि:


  • एक विशिष्ट वर्ग के लिए बाध्य
  • संपत्ति का नाम शामिल है।
  • टाइप है

मैं एक गटर का उल्लेख कैसे कर सकता हूं?


प्रॉक्सी


दिलचस्प तरीकों में से एक गटर कॉल श्रृंखला को बाधित करने के लिए ऑब्जेक्ट (या मोकिंग) है, जो कुछ पुस्तकालयों में उपयोग किया जाता है: मॉकिटो , क्वेरडीडीएसएल , बीनपाथ । हैबर पर आखिरी के बारे में लेखक का एक लेख था
विचार काफी सरल है, लेकिन लागू करने के लिए तुच्छ नहीं (उल्लेखित लेख से एक उदाहरण)।


 Account account = root(Account.class); tableBuilder.addColumn( $( account.getCustomer().getName() ) ); 

डायनेमिक कोड जेनरेशन का उपयोग करते हुए, एक विशेष प्रॉक्सी क्लास बनाई जाती है जो बीन क्लास से विरासत में मिलती है और श्रृंखला में सभी गेट्टर कॉल्स को स्वीकार करती है, जो थ्रेडलोकल वैरिएबल में एक पथ का निर्माण करती है। इस स्थिति में, ऑब्जेक्ट के इन गेटर्स का कॉल नहीं होता है।


इस लेख में हम एक वैकल्पिक विधि पर विचार करेंगे।


विधि लिंक


जावा 8 के आगमन के साथ, लंबोदा और विधि संदर्भों का उपयोग करने की क्षमता के साथ आया। इसलिए, ऐसा होना स्वाभाविक होगा:


 Person person = … assertEquals("name", $(Person::getName).getPath()); 

$ विधि निम्नलिखित लैम्ब्डा को स्वीकार करती है जिसमें गटर का संदर्भ पास होता है:


 public interface MethodReferenceLambda<BEAN, TYPE> extends Function<BEAN, TYPE>, Serializable {} ... public static <BEAN, TYPE> BeanProperty<BEAN, TYPE> $(MethodReferenceLambda<BEAN, TYPE> methodReferenceLambda) 

समस्या यह है कि प्रकार के क्षरण के कारण, रन-टाइम में BEAN और TYPE प्रकार प्राप्त करने का कोई तरीका नहीं है, और गेट नाम के बारे में भी कोई जानकारी नहीं है: विधि जिसे "बाहर" कहा जाता है, वह है।


फिर भी, एक निश्चित चाल है - यह क्रमबद्ध लैम्ब्डा का उपयोग है।


 MethodReferenceLambda<Person,String> lambda = Person::getName(); Method writeMethod = lambda.getClass().getDeclaredMethod("writeReplace"); writeMethod.setAccessible(true); SerializedLambda serLambda = (SerializedLambda) writeMethod.invoke(lambda); String className = serLambda.getImplClass().replaceAll("/", "."); String methodName = serLambda.getImplMethodName(); 

SerializedLambda वर्ग में वर्ग और विधि के बारे में सभी आवश्यक जानकारी होती है। आगे तकनीक का मामला है।
चूंकि मैं डेटा संरचनाओं के साथ बहुत काम करता हूं, इसलिए इस पद्धति ने मुझे संपत्तियों के स्थैतिक उपयोग के लिए एक छोटी सी लाइब्रेरी लिखने के लिए प्रोत्साहित किया।


बीनरफ लाइब्रेरी


पुस्तकालय का उपयोग कुछ इस तरह दिखता है:


 Person person = ... //     final BeanPath<Person, String> personCityProperty = $(Person::getContact).$(Contact::getAddress).$(Address::getCity); assertEquals("contact.address.city", personCityProperty.getPath()); 

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


आप getter name का उपयोग दोनों मानक संकेतन (getXXX () / isXXX ()) और गैर-मानक (xxx ()) में कर सकते हैं। पुस्तकालय संबंधित सेटर को खोजने की कोशिश करेगा, और यदि यह अनुपस्थित है, तो संपत्ति को केवल पढ़ने के लिए घोषित किया जाता है।


प्रदर्शन को गति देने के लिए, हल किए गए गुणों को कैश किया जाता है, और जब आप इसे फिर से उसी लैम्ब्डा के साथ कहते हैं, तो परिणाम पहले से ही सहेजा जाता है।


गुण / पथ नाम के अलावा, बीनपाथ ऑब्जेक्ट का उपयोग करके, आप ऑब्जेक्ट के संपत्ति मूल्य तक पहुंच सकते हैं:


 Person person = ... final BeanPath<Person, String> personCityProperty = $(Person::getContact).$(Contact::getAddress).$(Address::getCity); String personCity = personCityProperty.get(person); 

इसके अलावा, यदि श्रृंखला में मध्यवर्ती वस्तु अशक्त है, तो संबंधित कॉल भी NPE के बजाय अशक्त वापस आ जाएगी। यह सत्यापन की आवश्यकता के बिना कोड को बहुत सरल करेगा।


बीनपाथ के माध्यम से, आप एक वस्तु संपत्ति के मूल्य को बदल सकते हैं यदि यह केवल पढ़ने के लिए नहीं है:


 personCityProperty.set(person, “Madrid”); 

एक ही विचार के बाद - जितना संभव हो उतना कम एनपीई - इस मामले में, यदि श्रृंखला में मध्यवर्ती वस्तुओं में से एक शून्य है, तो पुस्तकालय स्वचालित रूप से इसे बनाने और इसे क्षेत्र में सहेजने का प्रयास करेगा। ऐसा करने के लिए, संबंधित गुण लेखन योग्य होना चाहिए, और ऑब्जेक्ट क्लास में मापदंडों के बिना एक सार्वजनिक निर्माता होना चाहिए।


प्रयोगात्मक विशेषता के रूप में, संग्रह के साथ काम करने का अवसर प्रदान किया जाता है। कुछ विशेष मामलों के लिए, कभी-कभी पथ का निर्माण करना आवश्यक होता है, जो संग्रह के भीतर वस्तुओं का जिक्र करता है। ऐसा करने के लिए, $ $ विधि प्रदान की जाती है, जो संग्रह के अंतिम तत्व के लिए एक लिंक का निर्माण करती है (इसे केवल एक ही माना जाता है)।


 final BeanPath<Person, String> personPhonePath = $(Person::getContact).$$(Contact::getPhoneList).$(Phone::getPhone); assertEquals("contact.phoneList.phone", personPhonePath.getPath()); assertEquals(personPhonePath.get(person), person.getContact().getPhoneList() .get(person.getContact().getPhoneList().size()-1).getPhone()); 

इस परियोजना को यहाँ होस्ट किया गया है: https://github.com/throwable/beanref , बायनेरिज़ jcenter maven repository से उपलब्ध हैं।


Poleznyashki


java.beans.Introspector
मानक जावा जावा से इंट्रोस्पेक्टर वर्ग आपको बिन गुणों को हल करने की अनुमति देता है।


अपाचे कॉमन्स बीनयूटिल्स
जावा बीन्स के साथ काम करने के लिए सबसे व्यापक पुस्तकालय।


BeanPath
उल्लेखित पुस्तकालय जो कि समीपता के माध्यम से ही करता है।


Objenesis
हम निर्माणकर्ताओं के किसी भी समूह के साथ किसी भी वर्ग की एक वस्तु को तुरंत हटा देते हैं।


QueryDSL उपनाम
QueryDSL में मानदंड निर्धारित करने के लिए अनुमानित कक्षाओं का उपयोग करना


Jinq
एक दिलचस्प पुस्तकालय जो जेपीए में मानदंड निर्धारित करने के लिए लंबोदा का उपयोग करता है। बहुत सारा जादू: प्रॉक्सिमिंग, क्रमबद्धता लैम्ब्डास, इंटरप्रेटिंग बाईटेकोड।

Source: https://habr.com/ru/post/hi469181/


All Articles