नमस्ते! चलिए दूसरी तरह की नेस्टेड क्लासेस के बारे में बात करते हैं। मैं स्थानीय कक्षाओं (विधि-स्थानीय आंतरिक कक्षाओं) के बारे में बात कर रहा हूं। गोता लगाने से पहले, हमें सबसे पहले नेस्टेड कक्षाओं की संरचना में उनकी जगह याद रखनी चाहिए।
हमारे आरेख से, हम देख सकते हैं कि स्थानीय वर्ग आंतरिक वर्गों की एक उप-प्रजाति है, जिसके बारे में हमने पिछली सामग्रियों में विस्तार से बात की थी । हालाँकि, स्थानीय वर्गों में कई महत्वपूर्ण विशेषताएं हैं और सामान्य आंतरिक वर्गों से अंतर हैं। मुख्य बात उनकी घोषणा में है: एक स्थानीय वर्ग केवल कोड के ब्लॉक में घोषित किया जाता है। बहुधा, यह घोषणा बाहरी वर्ग के किसी न किसी तरीके के अंदर होती है। उदाहरण के लिए, यह ऐसा दिखाई दे सकता है:

public class PhoneNumberValidator {
public void validatePhoneNumber(String number) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
// ...number validation code
}
}
महत्वपूर्ण!यदि आपके पास Java 7 स्थापित है, तो यह कोड IDEA में चिपकाए जाने पर संकलित नहीं होगा। इसके कारणों के बारे में हम पाठ के अंत में बात करेंगे। संक्षेप में, स्थानीय वर्ग कैसे काम करते हैं यह भाषा के संस्करण पर अत्यधिक निर्भर है। यदि यह कोड आपके लिए संकलित नहीं होता है, तो आप या तो भाषा संस्करण को आईडीईए में जावा 8 में स्विच कर सकते हैं, या शब्द को final
विधि पैरामीटर में जोड़ सकते हैं ताकि यह ऐसा दिखाई दे: validatePhoneNumber(final String number)
. उसके बाद सब काम हो जाएगा। यह एक छोटा प्रोग्राम है जो फ़ोन नंबरों को मान्य करता है। इसकी validatePhoneNumber()
विधि एक स्ट्रिंग को इनपुट के रूप में लेती है और यह निर्धारित करती है कि यह एक फ़ोन नंबर है या नहीं। और इस मेथड के अंदर, हमने अपने लोकल PhoneNumber
क्लास को डिक्लेयर किया। आप यथोचित पूछ सकते हैं कि क्यों। हम वास्तव में एक विधि के अंदर एक वर्ग क्यों घोषित करेंगे? सामान्य आंतरिक कक्षा का उपयोग क्यों नहीं करें? सच है, हम बना सकते थेPhoneNumber
वर्ग एक आंतरिक वर्ग। लेकिन अंतिम समाधान आपके कार्यक्रम की संरचना और उद्देश्य पर निर्भर करता है। आइए आंतरिक कक्षाओं पर एक पाठ से अपना उदाहरण याद करें:
public class Bicycle {
private String model;
private int maxWeight;
public Bicycle(String model, int maxWeight) {
this.model = model;
this.maxWeight = maxWeight;
}
public void start() {
System.out.println("Let's go!");
}
public class HandleBar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
}
इसमें हमने HandleBar
बाइक की इनर क्लास बनाई। क्या फर्क पड़ता है? सबसे पहले, कक्षा का उपयोग करने का तरीका अलग है। HandleBar
दूसरे उदाहरण में वर्ग पहले उदाहरण में वर्ग की तुलना में अधिक जटिल इकाई है PhoneNumber
। सबसे पहले, HandleBar
सार्वजनिक right
और left
विधियां हैं (ये सेटर्स/गेटर्स नहीं हैं)। दूसरा, पहले से भविष्यवाणी करना असंभव है कि हमें इसकी और इसके बाहरी Bicycle
वर्ग की आवश्यकता कहाँ हो सकती है। एक ही कार्यक्रम में दर्जनों अलग-अलग स्थान और तरीके हो सकते हैं। लेकिन PhoneNumber
कक्षा के साथ सबकुछ बहुत आसान है। हमारा कार्यक्रम बहुत ही सरल है। इसका केवल एक ही उद्देश्य है: यह जाँचना कि कोई नंबर वैध फ़ोन नंबर है या नहीं। ज्यादातर मामलों में, हमारेPhoneNumberValidator
एक स्टैंडअलोन प्रोग्राम भी नहीं होगा, बल्कि एक बड़े प्रोग्राम के लिए प्राधिकरण तर्क का एक हिस्सा होगा। उदाहरण के लिए, जब उपयोगकर्ता साइन अप करते हैं तो विभिन्न वेबसाइटें अक्सर फ़ोन नंबर मांगती हैं। यदि आप संख्याओं के बजाय कुछ बकवास दर्ज करते हैं, तो वेबसाइट एक त्रुटि रिपोर्ट करेगी: "यह फ़ोन नंबर नहीं है!" ऐसी वेबसाइट के डेवलपर्स (या बल्कि, इसके उपयोगकर्ता प्राधिकरण तंत्र) में हमारे जैसा कुछ शामिल हो सकता हैPhoneNumberValidator
उनके कोड में। दूसरे शब्दों में, हमारे पास एक विधि के साथ एक बाहरी वर्ग है, जिसका उपयोग कार्यक्रम में एक ही स्थान पर किया जाएगा और कहीं नहीं। और अगर इसका उपयोग किया जाता है, तो इसमें कुछ भी नहीं बदलेगा: एक तरीका अपना काम करता है - और वह यह है। इस मामले में, क्योंकि सभी तर्क एक विधि में एकत्रित होते हैं, वहां एक अतिरिक्त वर्ग को संपुटित करना कहीं अधिक सुविधाजनक और उचित होगा। गेट्टर और सेटर को छोड़कर इसका अपना कोई तरीका नहीं है। वास्तव में, हमें केवल कंस्ट्रक्टर से डेटा चाहिए। यह अन्य तरीकों में शामिल नहीं है। तदनुसार, इसका उपयोग करने वाली एकमात्र विधि के बाहर इसके बारे में जानकारी लेने का कोई कारण नहीं है। हमने एक उदाहरण भी दिया जिसमें एक स्थानीय वर्ग को एक विधि में घोषित किया जाता है, लेकिन यह एकमात्र विकल्प नहीं है। इसे केवल कोड ब्लॉक में घोषित किया जा सकता है:
public class PhoneNumberValidator {
{
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
}
public void validatePhoneNumber(String phoneNumber) {
// ...number validation code
}
}
या for
पाश में भी!
public class PhoneNumberValidator {
public void validatePhoneNumber(String phoneNumber) {
for (int i = 0; i < 10; i++) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
// ...some logic
}
// ...number validation code
}
}
लेकिन ऐसे मामले बेहद दुर्लभ हैं। ज्यादातर मामलों में, घोषणा विधि के अंदर होगी। इसलिए, हमने घोषणाओं का पता लगाया, और हमने "दर्शन" के बारे में भी बात की :) आंतरिक कक्षाओं की तुलना में स्थानीय वर्गों में क्या अतिरिक्त विशेषताएं और अंतर हैं? स्थानीय वर्ग का एक वस्तु उस विधि या ब्लॉक के बाहर नहीं बनाया जा सकता जिसमें इसे घोषित किया गया है। कल्पना कीजिए कि हमें एक ऐसी generatePhoneNumber()
विधि की आवश्यकता है जो एक यादृच्छिक फोन नंबर उत्पन्न करे और एक PhoneNumber
वस्तु लौटाए। हमारी वर्तमान स्थिति में, हम अपने सत्यापनकर्ता वर्ग में ऐसी कोई विधि नहीं बना सकते हैं:
public class PhoneNumberValidator {
public void validatePhoneNumber(String number) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
// ...number validation code
}
// Error! The compiler does not recognize the PhoneNumber class
public PhoneNumber generatePhoneNumber() {
}
}
स्थानीय वर्गों की एक अन्य महत्वपूर्ण विशेषता स्थानीय चर और विधि मापदंडों तक पहुँचने की क्षमता है। यदि आप भूल गए हैं, तो एक विधि के अंदर घोषित चर को "स्थानीय" चर के रूप में जाना जाता है। यही है, अगर हम किसी कारण से विधि String usCountryCode
के अंदर एक स्थानीय चर बनाते हैं validatePhoneNumber()
, तो हम इसे स्थानीय PhoneNumber
वर्ग से एक्सेस कर सकते हैं। हालाँकि, बहुत सारी सूक्ष्मताएँ हैं जो प्रोग्राम में प्रयुक्त भाषा के संस्करण पर निर्भर करती हैं। पाठ की शुरुआत में, हमने देखा कि किसी एक उदाहरण का कोड जावा 7 में संकलित नहीं हो सकता है, याद है? अब आइए इसके कारणों पर विचार करें :) जावा 7 में, एक स्थानीय वर्ग एक स्थानीय चर या विधि पैरामीटर का उपयोग केवल तभी कर सकता है जब उन्हें final
विधि के रूप में घोषित किया गया हो:
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
class PhoneNumber {
private String phoneNumber;
// Error! The method parameter must be declared as final!
public PhoneNumber() {
this.phoneNumber = number;
}
public void printUsCountryCode() {
// Error! The local variable must be declared as final!
System.out.println(usCountryCode);
}
}
// ...number validation code
}
यहाँ संकलक दो त्रुटियाँ उत्पन्न करता है। और यहाँ सब कुछ क्रम में है:
public void validatePhoneNumber(final String number) {
final String usCountryCode = "+1";
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public void printUsCountryCode() {
System.out.println(usCountryCode);
}
}
// ...number validation code
}
अब आप जानते हैं कि पाठ की शुरुआत से कोड संकलित क्यों नहीं होगा: जावा 7 में, एक स्थानीय वर्ग के पास केवल final
विधि पैरामीटर और final
स्थानीय चर तक पहुंच होती है। जावा 8 में, स्थानीय कक्षाओं का व्यवहार बदल गया है। भाषा के इस संस्करण में, एक स्थानीय वर्ग के पास न केवल final
स्थानीय चर और मापदंडों तक पहुंच है, बल्कि उन तक भी है जो हैं effective-final
। Effective-final
एक चर है जिसका मूल्य आरंभीकरण के बाद से नहीं बदला है। उदाहरण के लिए, जावा 8 में, हम usCountryCode
कंसोल पर चर को आसानी से प्रदर्शित कर सकते हैं, भले ही वह न हो final
। महत्वपूर्ण बात यह है कि इसका मूल्य नहीं बदलता है। निम्नलिखित उदाहरण में, सब कुछ वैसा ही काम करता है जैसा उसे करना चाहिए:
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
class PhoneNumber {
public void printUsCountryCode() {
// Java 7 would produce an error here
System.out.println(usCountryCode);
}
}
// ...number validation code
}
लेकिन अगर हम इनिशियलाइज़ेशन के तुरंत बाद वेरिएबल की वैल्यू बदलते हैं, तो कोड कंपाइल नहीं होगा।
public void validatePhoneNumber(String number) {
String usCountryCode = "+1";
usCountryCode = "+8";
class PhoneNumber {
public void printUsCountryCode() {
// Error!
System.out.println(usCountryCode);
}
}
// ...number validation code
}
कोई आश्चर्य नहीं कि स्थानीय वर्ग आंतरिक वर्ग की अवधारणा की एक उप-प्रजाति है! उनकी सामान्य विशेषताएं भी हैं। एक स्थानीय वर्ग के पास सभी (यहां तक कि निजी) क्षेत्रों और बाहरी वर्ग के तरीकों तक पहुंच होती है: स्थिर और गैर-स्थैतिक दोनों। उदाहरण के लिए, आइए String phoneNumberRegex
हमारे सत्यापनकर्ता वर्ग में एक स्थिर क्षेत्र जोड़ें:
public class PhoneNumberValidator {
private static String phoneNumberRegex = "[^0-9]";
public void validatePhoneNumber(String phoneNumber) {
class PhoneNumber {
// ......
}
}
}
इस स्थिर चर का उपयोग करके सत्यापन किया जाएगा। विधि जाँचती है कि क्या पास की गई स्ट्रिंग में ऐसे वर्ण हैं जो नियमित अभिव्यक्ति "" से मेल नहीं खाते हैं [^0-9]
(अर्थात, कोई भी वर्ण जो 0 से 9 तक का अंक नहीं है)। इस Variable को हम Local PhoneNumber
Class से आसानी से Access कर सकते हैं। उदाहरण के लिए, एक गेटर लिखें:
public String getPhoneNumberRegex() {
return phoneNumberRegex;
}
स्थानीय वर्ग आंतरिक कक्षाओं के समान हैं, क्योंकि वे किसी स्थिर सदस्य को परिभाषित या घोषित नहीं कर सकते हैं। स्थैतिक विधियों में स्थानीय वर्ग केवल संलग्न वर्ग के स्थिर सदस्यों को संदर्भित कर सकते हैं। उदाहरण के लिए, यदि आप संलग्न वर्ग के एक चर (फ़ील्ड) को स्थैतिक के रूप में परिभाषित नहीं करते हैं, तो जावा कंपाइलर एक त्रुटि उत्पन्न करता है: "स्थैतिक संदर्भ से गैर-स्थैतिक चर को संदर्भित नहीं किया जा सकता है।" स्थानीय वर्ग स्थिर नहीं हैं, क्योंकि उनके पास संलग्नक ब्लॉक में उदाहरण के सदस्यों तक पहुंच है। नतीजतन, उनमें अधिकांश प्रकार की स्थिर घोषणाएं नहीं हो सकती हैं। आप एक ब्लॉक के अंदर एक इंटरफेस घोषित नहीं कर सकते: इंटरफेस स्वाभाविक रूप से स्थिर हैं। यह कोड संकलित नहीं होता है:
public class PhoneNumberValidator {
public static void validatePhoneNumber(String number) {
interface I {}
class PhoneNumber implements I{
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
}
// ...number validation code
}
}
लेकिन अगर एक बाहरी वर्ग के भीतर एक इंटरफ़ेस घोषित किया जाता है, तो PhoneNumber
वर्ग इसे लागू कर सकता है:
public class PhoneNumberValidator {
interface I {}
public static void validatePhoneNumber(String number) {
class PhoneNumber implements I{
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
}
// ...number validation code
}
}
स्टेटिक इनिशियलाइज़र्स (इनिशियलाइज़ेशन ब्लॉक्स) या इंटरफेस को स्थानीय कक्षाओं में घोषित नहीं किया जा सकता है। लेकिन स्थानीय वर्गों में स्थिर सदस्य हो सकते हैं, बशर्ते कि वे निरंतर चर ( static final
) हों। और अब आप स्थानीय कक्षाओं के बारे में जानते हैं, दोस्तों! जैसा कि आप देख सकते हैं, उनमें सामान्य आंतरिक कक्षाओं से कई अंतर हैं। हमें यह समझने के लिए भाषा के विशिष्ट संस्करणों की विशेषताओं में भी तल्लीन करना पड़ा कि वे कैसे काम करते हैं :) अगले पाठ में, हम अनाम आंतरिक कक्षाओं के बारे में बात करेंगे - नेस्टेड कक्षाओं का अंतिम समूह। आपकी पढ़ाई में गुड लक! :)
GO TO FULL VERSION