"मैं आपको « एक्सेस मॉडिफायर्स » के बारे में बताने जा रहा हूं। मैंने उनके बारे में एक बार पहले भी बताया था, लेकिन दोहराव सीखने का एक स्तंभ है।"
आप पहुंच (दृश्यता) को नियंत्रित कर सकते हैं कि अन्य वर्गों के पास आपकी कक्षा के तरीकों और चर हैं। एक एक्सेस संशोधक प्रश्न का उत्तर देता है «कौन इस विधि/चर का उपयोग कर सकता है?»। आप प्रत्येक विधि या चर के लिए केवल एक संशोधक निर्दिष्ट कर सकते हैं।
1) « सार्वजनिक » संशोधक।
सार्वजनिक संशोधक के साथ चिह्नित एक चर, विधि या वर्ग को प्रोग्राम में कहीं से भी एक्सेस किया जा सकता है। यह खुलेपन की उच्चतम डिग्री है: इसमें कोई प्रतिबंध नहीं है।
2) « निजी » संशोधक।
निजी संशोधक के साथ चिह्नित एक चर, विधि या वर्ग को केवल उस वर्ग में पहुँचा जा सकता है जहाँ यह घोषित किया गया है। चिह्नित विधि या चर अन्य सभी वर्गों से छिपा हुआ है। यह गोपनीयता का उच्चतम स्तर है: केवल आपकी कक्षा द्वारा पहुँचा जा सकता है। इस तरह के तरीके विरासत में नहीं मिले हैं और इन्हें ओवरराइड नहीं किया जा सकता है। इसके अतिरिक्त, उन्हें एक वंश वर्ग में एक्सेस नहीं किया जा सकता है।
3) « डिफ़ॉल्ट संशोधक»।
यदि कोई चर या विधि किसी संशोधक के साथ चिह्नित नहीं है, तो इसे "डिफ़ॉल्ट" संशोधक के साथ चिह्नित माना जाता है। इस संशोधक के साथ चर और विधियाँ पैकेज में सभी वर्गों के लिए दृश्यमान हैं जहाँ उन्हें घोषित किया गया है, और केवल उन वर्गों के लिए। इस संशोधक को " पैकेज " या " पैकेज निजी " एक्सेस भी कहा जाता है , जो इस तथ्य की ओर इशारा करता है कि चर और विधियों तक पहुंच पूरे पैकेज के लिए खुली है जिसमें कक्षा शामिल है।
4) « संरक्षित » संशोधक।
पहुंच का यह स्तर पैकेज की तुलना में थोड़ा व्यापक है । संरक्षित संशोधक के साथ चिह्नित एक चर, विधि, या वर्ग को इसके पैकेज (जैसे "पैकेज"), और सभी विरासत में मिली कक्षाओं से एक्सेस किया जा सकता है।
यह तालिका यह सब समझाती है:
दृश्यता का प्रकार | कीवर्ड | पहुँच | |||
---|---|---|---|---|---|
आपकी कक्षा | आपका पैकेज | वंशज | सभी वर्ग | ||
निजी | निजी | हाँ | नहीं | नहीं | नहीं |
पैकेट | (कोई संशोधक नहीं) | हाँ | हाँ | नहीं | नहीं |
संरक्षित | संरक्षित | हाँ | हाँ | हाँ | नहीं |
जनता | जनता | हाँ | हाँ | हाँ | हाँ |
इस टेबल को आसानी से याद रखने का एक तरीका है। कल्पना कीजिए कि आप वसीयत लिख रहे हैं। आप अपनी सभी चीजों को चार श्रेणियों में बांट रहे हैं। आपकी चीजों का उपयोग कौन करता है?
किसके पास पहुंच है | संशोधक | उदाहरण |
---|---|---|
बस मैं | निजी | व्यक्तिगत पत्रिका |
परिवार | (कोई संशोधक नहीं) | परिवार की फ़ोटोज़ |
परिवार और वारिस | संरक्षित | पारिवारिक संपत्ति |
हर कोई | जनता | संस्मरण |
"यह कल्पना करने जैसा है कि एक ही पैकेज में कक्षाएं एक परिवार का हिस्सा हैं।"
"मैं आपको ओवरराइडिंग विधियों के बारे में कुछ दिलचस्प बारीकियाँ भी बताना चाहता हूँ।"
1) अमूर्त विधि का अंतर्निहित कार्यान्वयन।
मान लें कि आपके पास निम्न कोड है:
class Cat
{
public String getName()
{
return "Oscar";
}
}
और आपने एक टाइगर क्लास बनाने का फैसला किया है जो इस क्लास को इनहेरिट करता है, और नए क्लास में एक इंटरफ़ेस जोड़ता है
class Cat
{
public String getName()
{
return "Oscar";
}
}
interface HasName
{
String getName();
int getWeight();
}
class Tiger extends Cat implements HasName
{
public int getWeight()
{
return 115;
}
}
यदि आप IntelliJ IDEA द्वारा लागू करने के लिए बताए गए सभी लापता तरीकों को लागू करते हैं, तो बाद में आपको बग खोजने में लंबा समय लग सकता है।
यह पता चला है कि टाइगर क्लास में कैट से विरासत में मिली गेटनेम विधि है, जिसे हसनाम इंटरफ़ेस के लिए गेटनाम विधि के कार्यान्वयन के रूप में लिया जाएगा।
"मुझे इसके बारे में कुछ भी भयानक नहीं दिख रहा है।"
"यह बहुत बुरा नहीं है, यह गलतियों के रेंगने की संभावित जगह है।"
लेकिन यह और भी बुरा हो सकता है:
interface HasWeight
{
int getValue();
}
interface HasSize
{
int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
public int getValue()
{
return 115;
}
}
यह पता चला है कि आप हमेशा एकाधिक इंटरफेस से उत्तराधिकारी नहीं हो सकते हैं। अधिक सटीक रूप से, आप उन्हें इनहेरिट कर सकते हैं, लेकिन आप उन्हें सही तरीके से लागू नहीं कर सकते। उदाहरण देखें। दोनों इंटरफेस के लिए आवश्यक है कि आप getValue() विधि को लागू करें, लेकिन यह स्पष्ट नहीं है कि इसे क्या वापस करना चाहिए: वजन या आकार? इससे निपटना काफी अप्रिय है।
"मैं सहमत हूं। आप एक विधि को लागू करना चाहते हैं, लेकिन आप नहीं कर सकते। आप पहले से ही आधार वर्ग से समान नाम के साथ एक विधि प्राप्त कर चुके हैं। यह टूटा हुआ है।"
"लेकिन अच्छी खबर है।"
2) दृश्यता का विस्तार करना। जब आप किसी प्रकार को इनहेरिट करते हैं, तो आप किसी विधि की दृश्यता का विस्तार कर सकते हैं। यह कैसा दिखता है:
जावा कोड | विवरण |
---|---|
|
|
|
हमने विधि की दृश्यता को से protected तक बढ़ा दिया है public । |
कोड | यह «कानूनी» क्यों है |
---|---|
|
सब कुछ महान है। यहाँ हम यह भी नहीं जानते कि दृश्यता एक वंशज वर्ग में बढ़ा दी गई है। |
|
यहां हम उस विधि को कॉल करते हैं जिसकी दृश्यता बढ़ा दी गई है।
यदि यह संभव नहीं होता, तो हम हमेशा टाइगर में एक विधि घोषित कर सकते थे: दूसरे शब्दों में, हम किसी सुरक्षा उल्लंघन की बात नहीं कर रहे हैं। |
|
यदि बेस क्लास ( कैट ) में किसी विधि को कॉल करने के लिए आवश्यक सभी शर्तें संतुष्ट हैं, तो वे निश्चित रूप से अवरोही प्रकार ( टाइगर ) पर विधि को कॉल करने के लिए संतुष्ट हैं। क्योंकि विधि कॉल पर प्रतिबंध कमजोर थे, मजबूत नहीं। |
"मुझे यकीन नहीं है कि मैं पूरी तरह से समझ गया हूं, लेकिन मुझे याद होगा कि यह संभव है।"
3) रिटर्न प्रकार को कम करना।
एक ओवरराइड विधि में, हम रिटर्न प्रकार को संकुचित संदर्भ प्रकार में बदल सकते हैं।
जावा कोड | विवरण |
---|---|
|
|
|
हम विधि को ओवररोड करते हैं getMyParent , और अब यह एक Tiger वस्तु लौटाता है। |
कोड | यह «कानूनी» क्यों है |
---|---|
|
सब कुछ महान है। यहां हम यह भी नहीं जानते हैं कि getMyParent मेथड का रिटर्न टाइप डिसेंडेंट क्लास में चौड़ा कर दिया गया है।
कैसे «पुराना कोड» काम करता है और काम करता है। |
|
यहां हम उस विधि को कहते हैं जिसका रिटर्न प्रकार संकुचित हो गया है।
यदि यह संभव नहीं होता, तो हम हमेशा टाइगर में एक विधि की घोषणा कर दूसरे शब्दों में, कोई सुरक्षा उल्लंघन और/या टाइप कास्टिंग उल्लंघन नहीं हैं। |
|
और यहां सब कुछ ठीक काम करता है, हालांकि हमने वेरिएबल्स के प्रकार को बेस क्लास (कैट) तक चौड़ा कर दिया है।
ओवरराइडिंग के कारण, सही सेटमाईपेरेंट विधि को कॉल किया जाता है। और getMyParent मेथड को कॉल करते समय चिंता करने की कोई बात नहीं है , क्योंकि रिटर्न वैल्यू, टाइगर क्लास के बावजूद, बिना किसी समस्या के बेस क्लास (कैट) के myParent वेरिएबल को असाइन किया जा सकता है । टाइगर ऑब्जेक्ट्स को टाइगर वेरिएबल्स और कैट वेरिएबल्स दोनों में सुरक्षित रूप से स्टोर किया जा सकता है। |
"हां। समझ गया। तरीकों को ओवरराइड करते समय, आपको पता होना चाहिए कि यह सब कैसे काम करता है अगर हम अपनी वस्तुओं को कोड में पास करते हैं जो केवल बेस क्लास को संभाल सकता है और हमारी कक्षा के बारे में कुछ भी नहीं जानता है। "
"बिल्कुल सही! तब बड़ा सवाल यह है कि किसी विधि को ओवरराइड करते समय हम रिटर्न वैल्यू के प्रकार को कम क्यों नहीं कर सकते?"
"यह स्पष्ट है कि इस मामले में बेस क्लास में कोड काम करना बंद कर देगा:"
जावा कोड | समस्या की व्याख्या |
---|---|
|
|
|
हमने getMyParent मेथड को ओवरलोड कर दिया और इसके रिटर्न वैल्यू के प्रकार को कम कर दिया।
यहाँ सब कुछ ठीक है। |
|
तब यह कोड काम करना बंद कर देगा।
getMyParent मेथड किसी ऑब्जेक्ट के किसी भी इंस्टेंस को वापस कर सकता है, क्योंकि इसे वास्तव में टाइगर ऑब्जेक्ट पर कॉल किया जाता है। और हमारे पास असाइनमेंट से पहले चेक नहीं है। इस प्रकार, यह पूरी तरह से संभव है कि कैट-टाइप myParent चर एक स्ट्रिंग संदर्भ संग्रहीत करेगा। |
"अद्भुत उदाहरण, अमीगो!"
जावा में, किसी विधि को बुलाए जाने से पहले, कोई जांच नहीं होती है कि वस्तु में ऐसी कोई विधि है या नहीं। सभी चेक रनटाइम पर होते हैं। और एक [काल्पनिक] एक अनुपलब्ध विधि के लिए कॉल करने से प्रोग्राम को गैर-मौजूद बायटेकोड को निष्पादित करने का प्रयास करने की संभावना होगी। यह अंततः एक घातक त्रुटि का कारण बनेगा, और ऑपरेटिंग सिस्टम प्रोग्राम को जबरन बंद कर देगा।
"वाह। अब मुझे पता है।"