Kotlin में Data class: उन्हें इनहेरिट क्यों नहीं किया जा सकता और इसे कैसे हल करें (अंदर की कार्यप्रणाली का विश्लेषण)
\n\nक्या आपने कभी data class User(val name: String) लिखा है, और फिर data class Admin extends User बनाना चाहा है? और कंपाइलर से लाल त्रुटि मिली? यदि हाँ — तो आप अकेले नहीं हैं। «Extend data class in Kotlin» का प्रश्न Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है (50,000 से अधिक बार देखा गया)।
आज हम केवल तैयार उत्तर नहीं देंगे। हम Kotlin के अंदर झाँकेंगे, समझेंगे कि भाषा के निर्माताओं ने इस तरह का «कठोर» प्रतिबंध क्यों लगाया, और आर्किटेक्चर को तोड़े बिना इसे हल करने के 3 कानूनी तरीके दिखाएँगे।
\n\nयह लेख शैक्षिक पोर्टल PythonLib के लिए लिखा गया है — यहाँ हम केवल स्निपेट कॉपी करने के बजाय गहराई से खोज करना पसंद करते हैं।
\n\ndata class को इनहेरिट क्यों नहीं किया जा सकता? बाइट-कोड में झाँकते हैं
\n\nचलिए सीधे मुद्दे पर आते हैं। एक सरल data class लिखते हैं और देखते हैं कि कंपाइलर क्या उत्पन्न करता है:
\n\ndata class User(val name: String, val age: Int)\n\nKotlin अंदर से यह बनाता है:
\n- \n
equals()— सभी फ़ील्ड्स की तुलना करता है \n hashCode()— सभी फ़ील्ड्स के आधार पर हैश \n toString()— सुंदर स्ट्रिंग प्रस्तुति \n copy()— फ़ील्ड्स बदलने के साथ कॉपी करने का फ़ंक्शन \n componentN()— डीस्ट्रक्चरिंग \n
अब कल्पना करें कि आप data class को इनहेरिट करते हैं:
\n\nopen data class Base(val x: Int)\ndata class Derived(val x: Int, val y: Int) : Base(x) // कंपाइलेशन त्रुटि!\n\nKotlin कहता है: «Inheritance of data classes is not allowed»। क्यों? आइए समझते हैं।
\n\nसमस्या #1: copy() टूट जाता है। फ़ंक्शन copy() केवल वर्तमान क्लास के फ़ील्ड्स के लिए उत्पन्न होता है। यदि आपके पास Base से इनहेरिट किया गया Derived है, तो Base में copy() को y फ़ील्ड के बारे में पता नहीं है। base.copy() कॉल करने पर, आप इनहेरिटर का डेटा खो देंगे।
समस्या #2: equals() और hashCode()। कल्पना करें: Base(1) और Derived(1, 2)। क्या ये बराबर होने चाहिए? यदि हाँ — तो समरूपता टूट जाती है (क्योंकि Derived में अधिक फ़ील्ड्स हैं)। यदि नहीं — तो equals/hashCode का अनुबंध टूट जाता है। Kotlin ने सुरक्षित रास्ता चुना — इनहेरिटेंस पर प्रतिबंध लगा दिया।
समस्या #3: componentN()। val (x) = base और val (x, y) = derived — कंपोनेंट्स की अलग-अलग संख्याएँ। इनहेरिटेंस से भ्रम पैदा होगा।
निष्कर्ष: data class अपरिवर्तनीय डेटा कंटेनरों के लिए बनाया गया है, न कि पदानुक्रमों के लिए। यह भाषा डिज़ाइन का एक सचेत निर्णय है।
\n\nतरीका #1: इनहेरिटेंस के बजाय कम्पोज़ीशन (अनुशंसित)
\n\nसबसे साफ तरीका — इनहेरिट न करें, बल्कि एक data class को दूसरे में शामिल करें। इसे कम्पोज़ीशन कहते हैं।
\n\ndata class Address(val city: String, val street: String)\n\ndata class User(\n val name: String,\n val age: Int,\n val address: Address // कम्पोज़ीशन\n)\n\nfun main() {\n val address = Address("मॉस्को", "टावर्सकाया")\n val user = User("अन्ना", 25, address)\n println(user) // User(name=अन्ना, age=25, address=Address(city=मॉस्को, street=टावर्सकाया))\n \n // कॉपी करना पूरी तरह से काम करता है\n val user2 = user.copy(age = 26)\n println(user2) // User(name=अन्ना, age=26, address=Address(city=मॉस्को, street=टावर्सकाया))\n}\n\n\nलाभ: सब कुछ «बॉक्स से बाहर» काम करता है, equals/hashCode में कोई आश्चर्य नहीं, कोड पारदर्शी है।
\n\nनुकसान: यदि आपको कई स्तरों की नेस्टिंग चाहिए, तो «मैत्रियोश्का» बनता है। लेकिन इसे डेटा सामान्यीकरण द्वारा हल किया जा सकता है।
\n\nतरीका #2: Sealed class + data class (सख्त पदानुक्रमों के लिए)
\n\nयदि आपको वास्तव में पदानुक्रम चाहिए (उदाहरण के लिए, विभिन्न प्रकार के उपयोगकर्ता), तो sealed class (या sea का उपयोग करें)