kotlin - कोटलिन एक्सटेंशन कार्यों को कब पसंद करना चाहिए?




code-structure (3)

एक्सटेंशन फ़ंक्शन वास्तव में सुरक्षित कॉल ऑपरेटर के साथ अच्छी तरह से खेलते हैं ?. । यदि आप उम्मीद करते हैं कि फ़ंक्शन का तर्क कभी-कभी null होगा, तो जल्दी लौटने के बजाय, इसे एक्सटेंशन फ़ंक्शन का रिसीवर बनाएं।

साधारण कार्य:

fun nullableSubstring(s: String?, from: Int, to: Int): String? {
    if (s == null) {
        return null
    }

    return s.substring(from, to)
}

विस्तार समारोह:

fun String.extensionSubstring(from: Int, to: Int) = substring(from, to)

कॉल साइट:

fun main(args: Array<String>) {
    val s: String? = null

    val maybeSubstring = nullableSubstring(s, 0, 1)
    val alsoMaybeSubstring = s?.extensionSubstring(0, 1)

जैसा कि आप देख सकते हैं, दोनों एक ही काम करते हैं, हालांकि विस्तार फ़ंक्शन कम है और कॉल साइट पर, यह तुरंत स्पष्ट है कि परिणाम अशक्त होगा।

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

स्कोपिंग के रूप में, कोई अंतर नहीं लगता है: दोनों को अंदर या बाहर की कक्षाओं और अन्य कार्यों के रूप में घोषित किया जा सकता है और दोनों में समान रूप से दृश्यता संशोधक हो सकते हैं या नहीं।

भाषा संदर्भ विभिन्न स्थितियों के लिए नियमित कार्यों या विस्तार कार्यों का उपयोग करने की अनुशंसा नहीं करता है।

तो, मेरा सवाल यह है: जब विस्तार के कार्य नियमित गैर-सदस्य वाले से अधिक लाभ देते हैं? और जब एक्सटेंशन पर नियमित होते हैं?

foo.bar(baz, baq) बनाम bar(foo, baz, baq)

क्या यह केवल एक फ़ंक्शन शब्दार्थ का संकेत है (रिसीवर निश्चित रूप से फ़ोकस में है) या क्या ऐसे मामले हैं जब एक्सटेंशन फ़ंक्शंस का उपयोग करके कोड बहुत अधिक क्लीनर बनाता है / अवसरों को खोलता है?


ऐसे मामले हैं जहां आपको एक्सटेंशन विधियों का उपयोग करना होगा। जैसे यदि आपके पास कुछ सूची कार्यान्वयन MyList<T> , तो आप एक विस्तार विधि लिख सकते हैं जैसे

fun Int MyList<Int>.sum() { ... }

इसे "सामान्य" विधि के रूप में लिखना असंभव है।


विस्तार कार्य कुछ मामलों में उपयोगी होते हैं, और दूसरों में अनिवार्य होते हैं:

मुहावरेदार मामले:

  1. जब आप मौजूदा API को बढ़ाना, बढ़ाना या बदलना चाहते हैं। एक एक्सटेंशन फ़ंक्शन नई कार्यक्षमता को जोड़कर एक वर्ग को बदलने का मुहावरेदार तरीका है। आप एक्सटेंशन फ़ंक्शन और एक्सटेंशन गुण जोड़ सकते हैं। जैक्सन-कोटलिन मॉड्यूल में TypeReference क्लास में तरीके जोड़ने के लिए एक उदाहरण देखें TypeReference और जेनेरिक की हैंडलिंग को सरल TypeReference

  2. नए या मौजूदा तरीकों से अशक्त सुरक्षा को जोड़ना, जिन्हें null नहीं कहा जा सकता। उदाहरण के लिए स्ट्रिंग के String?.isNullOrBlank() लिए एक्सटेंशन फ़ंक्शन String?.isNullOrBlank() आपको एक फ़ंक्शन का उपयोग करने की अनुमति देता है यहां तक ​​कि एक null स्ट्रिंग पर भी अपना स्वयं का null चेक करने के लिए। आंतरिक कार्यों को कॉल करने से पहले फ़ंक्शन स्वयं चेक करता है। Nullable रिसीवर के साथ एक्सटेंशन के लिए दस्तावेज़ देखें

अनिवार्य मामले:

  1. जब आप इंटरफ़ेस के लिए इनलाइन डिफ़ॉल्ट फ़ंक्शन चाहते हैं, तो आपको इंटरफ़ेस में जोड़ने के लिए एक एक्सटेंशन फ़ंक्शन का उपयोग करना होगा क्योंकि आप इंटरफ़ेस घोषणा के भीतर ऐसा नहीं कर सकते हैं (इनलाइन फ़ंक्शन final होना चाहिए जो वर्तमान में इंटरफ़ेस के भीतर अनुमति नहीं है)। यह उपयोगी है जब आपको इनलाइन रीफाइंड फ़ंक्शन की आवश्यकता होती है, उदाहरण के लिए इनजेक से यह कोड

  2. जब आप for (item in collection) { ... } लिए जोड़ना चाहते हैं for (item in collection) { ... } एक वर्ग का समर्थन करते हैं जो वर्तमान में उस उपयोग का समर्थन नहीं करता है। आप एक iterator() एक्सटेंशन विधि जोड़ सकते हैं जो लूप डॉक्यूमेंटेशन के लिए बताए गए नियमों का पालन करता है - यहां तक ​​कि लौटाए गए इट्रेटर जैसी ऑब्जेक्ट next() और hasNext() प्रदान करने के नियमों को संतुष्ट करने के लिए एक्सटेंशन का उपयोग कर सकते हैं।

  3. मौजूदा वर्गों जैसे + और * लिए ऑपरेटरों को जोड़ना (# 1 का विशेषज्ञता लेकिन आप इसे किसी अन्य तरीके से नहीं कर सकते, इसलिए अनिवार्य है)। ऑपरेटर ओवरलोडिंग के लिए प्रलेखन देखें

वैकल्पिक मामले:

  1. जब आप किसी कॉल करने वाले को कुछ दिखाई देते हैं, तो आप उसे नियंत्रित करना चाहते हैं, इसलिए आप केवल उसी संदर्भ में कक्षा का विस्तार करते हैं जिसमें आप कॉल को दृश्यमान होने देंगे। यह वैकल्पिक है क्योंकि आप केवल एक्सटेंशन को हमेशा देखने की अनुमति दे सकते हैं। विस्तार कार्यों के लिए अन्य SO प्रश्न में उत्तर देखें

  2. आपके पास एक इंटरफ़ेस है जिसे आप आवश्यक कार्यान्वयन को सरल बनाना चाहते हैं, जबकि अभी भी उपयोगकर्ता के लिए अधिक आसान सहायक कार्यों की अनुमति देता है। इंटरफ़ेस के गैर-अपेक्षित-से-कार्यान्वित भागों को जोड़ने के लिए आप इंटरफ़ेस के लिए वैकल्पिक तरीकों को वैकल्पिक रूप से जोड़ सकते हैं, या विस्तार कार्यों का उपयोग कर सकते हैं। एक चूक को ओवरराइड करने की अनुमति देता है, दूसरा नहीं करता है (एक्सटेंशन बनाम सदस्यों की पूर्वता को छोड़कर)।

  3. जब आप फ़ंक्शन को कार्यक्षमता की श्रेणी से संबंधित करना चाहते हैं; एक्सटेंशन फ़ंक्शंस उनके रिसीवर क्लास को एक ऐसी जगह के रूप में इस्तेमाल करते हैं जहाँ से उन्हें ढूंढा जा सके। उनका नाम स्थान वर्ग (या कक्षाएं) बन जाता है, जहां से उन्हें ट्रिगर किया जा सकता है। जबकि शीर्ष स्तर के कार्य खोजने में कठिन होंगे, और आईडीई कोड पूरा होने वाले संवादों में वैश्विक नाम स्थान को भर देंगे। आप मौजूदा लाइब्रेरी नाम स्थान समस्याओं को भी ठीक कर सकते हैं। उदाहरण के लिए, जावा 7 में आपके पास Path क्लास है और Files.exist(path) मेथड को खोजना मुश्किल है क्योंकि यह अजीब तरह से नाम है। फ़ंक्शन को इसके बजाय सीधे Path.exists() पर रखा जा सकता है। (@Kirill)

वरीयता नियम:

मौजूदा कक्षाओं का विस्तार करते समय, पूर्ववर्ती नियमों को ध्यान में रखें। वे KT-10806 में वर्णित हैं:

वर्तमान संदर्भ में प्रत्येक अंतर्निहित रिसीवर के लिए हम सदस्यों की कोशिश करते हैं, फिर स्थानीय एक्सटेंशन फ़ंक्शंस (भी पैरामीटर जिसमें एक्सटेंशन फ़ंक्शन होता है), फिर गैर-स्थानीय एक्सटेंशन।







code-structure