जावा संस्कृति के बारे में बहुत सी बातें कही जा सकती हैं, लेकिन मुझे लगता है कि जिस स्थिति में आप अभी सामना कर रहे हैं, उसके कुछ महत्वपूर्ण पहलू हैं:
- लाइब्रेरी कोड एक बार लिखा जाता है, लेकिन अधिक बार उपयोग किया जाता है। हालांकि लाइब्रेरी लिखने के ओवरहेड को कम से कम करना अच्छा है, यह लंबे समय में लिखने के तरीके में शायद अधिक सार्थक है जो लाइब्रेरी का उपयोग करने के ओवरहेड को कम करता है।
- इसका मतलब है कि स्व-दस्तावेजीकरण प्रकार महान हैं: विधि के नाम यह स्पष्ट करने में मदद करते हैं कि क्या हो रहा है और आप किसी वस्तु से बाहर निकल रहे हैं।
- कुछ प्रकार की त्रुटियों को दूर करने के लिए स्टेटिक टाइपिंग एक बहुत ही उपयोगी उपकरण है। यह निश्चित रूप से सब कुछ ठीक नहीं करता है (लोग हास्केल के बारे में मज़ाक करना पसंद करते हैं कि एक बार जब आपको अपना कोड स्वीकार करने के लिए टाइप सिस्टम मिलता है, तो शायद यह सही है), लेकिन यह कुछ प्रकार की गलत चीजों को असंभव बनाना बहुत आसान बनाता है।
- लाइब्रेरी कोड लिखना अनुबंधों को निर्दिष्ट करने के बारे में है। आपके तर्क और परिणाम प्रकारों के लिए अंतर को परिभाषित करना आपके अनुबंधों की सीमाओं को अधिक स्पष्ट रूप से परिभाषित करता है। यदि कोई चीज़ स्वीकार करता है या टूपल पैदा करता है, तो यह कहने में कोई गुरेज नहीं है कि यह वास्तव में आपको प्राप्त होना चाहिए या उत्पादन करना चाहिए या नहीं, और इस तरह के सामान्य प्रकारों पर अड़चनें बहुत कम हैं (क्या इसमें तत्वों की सही संख्या भी है?) उस प्रकार के जो आप उम्मीद कर रहे थे?)।
खेतों के साथ "संरचना" कक्षाएं
जैसा कि अन्य उत्तरों ने उल्लेख किया है, आप सार्वजनिक क्षेत्रों के साथ एक वर्ग का उपयोग कर सकते हैं। यदि आप इन्हें अंतिम रूप देते हैं, तो आपको एक अपरिवर्तनीय वर्ग मिलता है, और आप उन्हें निर्माणकर्ता के साथ आरंभ करेंगे:
class ParseResult0 {
public final long millis;
public final boolean isSeconds;
public final boolean isLessThanOneMilli;
public ParseResult0(long millis, boolean isSeconds, boolean isLessThanOneMilli) {
this.millis = millis;
this.isSeconds = isSeconds;
this.isLessThanOneMilli = isLessThanOneMilli;
}
}
बेशक, इसका मतलब है कि आप एक विशेष वर्ग से बंधे हैं, और कुछ भी जो कभी भी पार्स परिणाम का उत्पादन या उपभोग करने की आवश्यकता होती है उसे इस वर्ग का उपयोग करना होगा। कुछ अनुप्रयोगों के लिए, यह ठीक है। दूसरों के लिए, यह कुछ दर्द पैदा कर सकता है। बहुत जावा कोड अनुबंधों को परिभाषित करने के बारे में है, और यह आमतौर पर आपको इंटरफेस में ले जाएगा।
एक और नुकसान यह है कि एक वर्ग आधारित दृष्टिकोण के साथ, आप क्षेत्रों को उजागर कर रहे हैं और उन सभी क्षेत्रों में मान होना चाहिए । जैसे, isSeconds और millis में हमेशा कुछ value होती है, भले ही isLessThanOneMilli सच हो। जब isLessThanOneMilli सच है तो मिलिस फ़ील्ड के मूल्य की व्याख्या क्या होनी चाहिए?
इंटरफेसेस के रूप में "स्ट्रक्चर्स"
इंटरफेस में अनुमत स्थैतिक तरीकों के साथ, वास्तव में बहुत सारे सिंटैक्टिक ओवरहेड के बिना अपरिवर्तनीय प्रकार बनाना आसान है। उदाहरण के लिए, मैं इस तरह की परिणाम संरचना को लागू कर सकता हूं जैसे कि आप कुछ इस तरह से बोल रहे हैं:
interface ParseResult {
long getMillis();
boolean isSeconds();
boolean isLessThanOneMilli();
static ParseResult from(long millis, boolean isSeconds, boolean isLessThanOneMill) {
return new ParseResult() {
@Override
public boolean isSeconds() {
return isSeconds;
}
@Override
public boolean isLessThanOneMilli() {
return isLessThanOneMill;
}
@Override
public long getMillis() {
return millis;
}
};
}
}
यह अभी भी बहुत सारे बॉयलरप्लेट है, मैं बिल्कुल सहमत हूं, लेकिन इसके कुछ फायदे भी हैं, और मुझे लगता है कि उन लोगों को आपके कुछ मुख्य सवालों का जवाब मिलना शुरू हो जाता है।
इस पार्स परिणाम जैसी संरचना के साथ, आपके पार्सर के अनुबंध को बहुत स्पष्ट रूप से परिभाषित किया गया है। पायथन में, एक ट्यूपल वास्तव में दूसरे टपल से अलग नहीं है। जावा में, स्थैतिक टाइपिंग उपलब्ध है, इसलिए हम पहले से ही त्रुटियों के कुछ वर्गों को नियंत्रित करते हैं। उदाहरण के लिए, यदि आप पायथन में एक टपल लौटा रहे हैं, और आप टपल लौटना चाहते हैं (मिलिस, आईस्कॉन्ड्स, isLessThanOneMilli), तो आप गलती से कर सकते हैं:
return (true, 500, false)
जब आपका मतलब था:
return (500, true, false)
इस तरह के जावा इंटरफेस के साथ, आप संकलन नहीं कर सकते:
return ParseResult.from(true, 500, false);
बिल्कुल भी। आप को करना पड़ेगा:
return ParseResult.from(500, true, false);
यह सामान्य रूप से सांख्यिकीय रूप से टाइप की गई भाषाओं का लाभ है।
यह दृष्टिकोण आपको उन मूल्यों को प्रतिबंधित करने की क्षमता भी प्रदान करना शुरू कर देता है जिन्हें आप प्राप्त कर सकते हैं। उदाहरण के लिए, जब getMillis () को कॉल किया जाता है, तो आप जांच सकते हैं कि क्या isLessThanOneMilli () सही है, और यदि यह है, तो एक IllegalStateException (उदाहरण के लिए) को फेंक दें, क्योंकि उस मामले में मिली का कोई सार्थक मूल्य नहीं है।
यह मुश्किल से गलत बात कर रही है
ऊपर दिए गए इंटरफ़ेस उदाहरण में, आपके पास अभी भी समस्या है कि आप गलती से isSeconds और isLessThanOneMilli तर्कों को स्वैप कर सकते हैं, हालांकि, क्योंकि उनके पास एक ही प्रकार है।
व्यवहार में, आप वास्तव में TimeUnit और अवधि का उपयोग करना चाहते हैं, ताकि आपके पास एक परिणाम हो जैसे:
interface Duration {
TimeUnit getTimeUnit();
long getDuration();
static Duration from(TimeUnit unit, long duration) {
return new Duration() {
@Override
public TimeUnit getTimeUnit() {
return unit;
}
@Override
public long getDuration() {
return duration;
}
};
}
}
interface ParseResult2 {
boolean isLessThanOneMilli();
Duration getDuration();
static ParseResult2 from(TimeUnit unit, long duration) {
Duration d = Duration.from(unit, duration);
return new ParseResult2() {
@Override
public boolean isLessThanOneMilli() {
return false;
}
@Override
public Duration getDuration() {
return d;
}
};
}
static ParseResult2 lessThanOneMilli() {
return new ParseResult2() {
@Override
public boolean isLessThanOneMilli() {
return true;
}
@Override
public Duration getDuration() {
throw new IllegalStateException();
}
};
}
}
यह एक बहुत अधिक कोड हो रहा है, लेकिन आपको केवल इसे एक बार लिखने की ज़रूरत है, और (यह मानते हुए कि आपने चीजों को ठीक से दस्तावेज किया है), आपके कोड का उपयोग करने वाले लोगों को यह अनुमान लगाने की ज़रूरत नहीं है कि परिणाम का क्या मतलब है, और गलती से उन चीजों की तरह नहीं कर सकते result[0]
जब उनका मतलब है result[1]
। आप अभी भी बहुत आसानी से उदाहरण बनाने के लिए मिलता है , और उनमें से डेटा प्राप्त करना यह सब कठिन नहीं है:
ParseResult2 x = ParseResult2.from(TimeUnit.MILLISECONDS, 32);
ParseResult2 y = ParseResult2.lessThanOneMilli();
ध्यान दें कि आप वास्तव में वर्ग आधारित दृष्टिकोण के साथ भी कुछ ऐसा कर सकते हैं। बस विभिन्न मामलों के लिए निर्माता निर्दिष्ट करें। आपके पास अभी भी इस बात का मुद्दा है कि अन्य क्षेत्रों को आरंभिक रूप से क्या बताया जाए, हालांकि, और आप उन तक पहुंच नहीं रोक सकते।
एक अन्य जवाब में उल्लेख किया गया है कि जावा के उद्यम-प्रकार की प्रकृति का अर्थ है कि आप उस समय के लिए अन्य पुस्तकालयों की रचना कर रहे हैं, जो पहले से मौजूद हैं, या अन्य लोगों के लिए पुस्तकालयों का उपयोग करने के लिए लिख रहे हैं। आपके सार्वजनिक एपीआई को परिणाम टाइप करने के लिए प्रलेखन से परामर्श करने के लिए बहुत समय की आवश्यकता नहीं होनी चाहिए यदि इसे टाला जा सकता है।
आप केवल एक बार इन संरचनाओं को लिखते हैं , लेकिन आप उन्हें कई बार बनाते हैं, इसलिए आप अभी भी चाहते हैं कि संक्षिप्त रचना (जो आपको मिलती है)। स्टैटिक टाइपिंग सुनिश्चित करता है कि जो डेटा आप उनसे प्राप्त कर रहे हैं, वह वही है जो आप अपेक्षा करते हैं।
अब, सभी ने कहा कि, अभी भी ऐसे स्थान हैं जहां सरल ट्यूपल या सूचियां बहुत कुछ समझ सकती हैं। किसी चीज़ की एक सरणी को लौटाने में कम ओवरहेड हो सकता है, और यदि ऐसा है (और यह ओवरहेड महत्वपूर्ण है, जिसे आप प्रोफाइलिंग के साथ निर्धारित करेंगे), तो एक सरल सरणी का उपयोग करके आंतरिक रूप से बहुत सारे अर्थ हो सकते हैं। आपके सार्वजनिक एपीआई को अभी भी स्पष्ट रूप से परिभाषित प्रकार होना चाहिए।