2015-08-23 6 views
31

के रूप में पारित ऑब्जेक्ट को संशोधित करती है या नहीं, मैं एक सी ++ पृष्ठभूमि से आया हूं और मैं वर्तमान में जावा सीख रहा हूं। एक प्रश्न तब उठ गया जब मैंने कुछ तीसरे पक्ष के पुस्तकालयों का उपयोग करने की कोशिश की। मैं कैसे निर्धारित करूं कि पैरामीटर के रूप में ऑब्जेक्ट संदर्भ लेने वाली विधि को कॉल ऑब्जेक्ट को संशोधित करता है या नहीं? सी ++ में यह कॉन्स कीवर्ड के उपयोग के लिए स्पष्ट धन्यवाद है। अगर विधि हस्ताक्षर है:यह निर्धारित करने के लिए कि जावा विधि पैरामीटर

void foo(Boo& boo); 

मैं, पता है कि संदर्भित वस्तु संशोधित किया जा सकता है, जबकि यदि विधि हस्ताक्षर है:

void foo(const Boo& boo); 

संकलक गारंटी देता है कि संदर्भित वस्तु संशोधित नहीं है।

मैंने जावा में कुछ समान नहीं देखा है, क्योंकि केवल संदर्भ ही अंतिम घोषित किया जा सकता है, संदर्भित वस्तु नहीं, और अंतिम तर्क पहली जगह में बहुत अधिक समझ में नहीं आता है क्योंकि यह वैसे भी मूल्य से गुजरता है । इसलिए, जब मैं इस तरह के रूप में एक विधि देखें:

void foo(Boo boo) {...} 

किस तरह तय करते मैं अगर वस्तु बू द्वारा संदर्भित समारोह के शरीर के अंदर संशोधित किया गया है (शायद एनोटेशन का उपयोग)? अगर जानने का कोई तरीका नहीं है, तो भ्रम और बग से बचने के लिए कुछ व्यापक रूप से उपयोग किए जाने वाले सम्मेलन या कुछ सर्वोत्तम प्रथाएं हैं?

+4

दस्तावेज़ों को पढ़ें। (आप C++ के सापेक्ष जावा में यह कमजोरी मान सकते हैं, लेकिन हम वास्तव में इसे याद नहीं करते हैं, सी ++ में काम करते समय आप चेक अपवादों को याद करते हैं।) – user2357112

+2

सबसे अच्छा अभ्यास जहां संभव हो वहां उत्परिवर्तन से बचने के लिए है। यह सब कुछ के लिए जाता है, सिर्फ विधियों के लिए तर्क के लिए नहीं। – usr

+0

@ user2357112 "* सी ++ में काम करते समय चेक अपवादों को याद करने से कहीं अधिक * * प्रोग्राम प्रवाह को नियंत्रित करने के लिए अपवादों का उपयोग करना एक त्रुटि है, असाधारण परिस्थितियों ** के लिए अपवाद (जैसा कि इसका नाम कहता है) है। एक भाषा सुविधा होने का एकमात्र उद्देश्य अपवादों का उपयोग करके त्रुटि से पुनर्प्राप्त करने में सक्षम होना है क्योंकि संचार चैनल "प्रवाह नियंत्रण के अपवाद" का एक और मामला है, जो राज्यों के ऊपर नियम के रूप में एक डिज़ाइन त्रुटि है। हम चेक अपवादों को याद नहीं करते हैं। – Manu343726

उत्तर

13

दुर्भाग्यवश, भाषा में ऐसी कोई सुविधा नहीं बनाई गई है। एक अच्छी रक्षात्मक अभ्यास आपके द्वारा अपरिवर्तनीय डेटा ऑब्जेक्ट को परिभाषित करना है (यानी, बिना किसी सार्वजनिक विधि के जो उनके राज्य को संशोधित करने की अनुमति देता है)। यदि आप वास्तव में इस बारे में चिंतित हैं, तो आप उस विधि को पार करने से पहले किसी ऑब्जेक्ट को कॉपी/क्लोन कर सकते हैं जिस पर आप भरोसा नहीं करते हैं, लेकिन यह आमतौर पर एक अनावश्यक सावधानी है।

24

मैं कैसे निर्धारित करूं कि बू द्वारा संदर्भित ऑब्जेक्ट फ़ंक्शन के शरीर (शायद एनोटेशन का उपयोग करके) में संशोधित किया गया है या नहीं?

दुर्भाग्य से कोड को पढ़ने का एकमात्र तरीका है।

यदि पता करने का कोई तरीका नहीं है, तो भ्रम और बग से बचने के लिए कुछ व्यापक रूप से उपयोग किए जाने वाले सम्मेलन या कुछ सर्वोत्तम प्रथाएं हैं?

आम सम्मेलन एक वस्तु को पारित करना है जिसे संशोधित नहीं किया जा सकता है, यदि आवश्यक हो तो एक रैपर का उपयोग कर। यह सुनिश्चित करता है कि वर्ग वस्तु को संशोधित नहीं कर सकता है।

List<String> readOnly = Collections.unmodifiableList(list); 

तो वस्तु Cloneable है, आप भी clone() उपयोग कर सकते हैं लेकिन एक और आम दृष्टिकोण एक प्रति का प्रयोग है।

List<String> readOnly = new ArrayList<>(list); 

आप इस तरह के व्यवहार के बारे में परवाह है, तो इकाई परीक्षण एक विधि एक वस्तु को संशोधित करता है या नहीं दिखा सकते हैं। यदि आपके पास पहले से ही यूनिट परीक्षण हैं, तो आमतौर पर यह जांचने के लिए एक या दो पंक्तियां अतिरिक्त होती हैं।

+1

सी ++ से माइग्रेट करने वाले किसी व्यक्ति के लिए सलाह के रूप में, यह उल्लेख करना महत्वपूर्ण होगा कि आपको जावा में 'क्लोन' का उपयोग नहीं करना चाहिए, क्योंकि [idiom टूटा हुआ है] (http://www.artima.com/intv/bloch13। एचटीएमएल)। –

+0

@ मिकमोनेमिक आपको क्लोन() को कॉल करने से बचना चाहिए यदि उसके कार्यान्वयन में कोई बग है। यदि ऑब्जेक्ट पहले से ही क्लोनेबल नहीं है, तो इसे क्लोनेबल जटिल वस्तुओं के लिए सही esp प्राप्त करना मुश्किल है और गैर-तुच्छ वस्तुओं के लिए टालना चाहिए। मैंने कई बगों को देखा है जो हल हो चुके थे 'क्लोन()' पहले स्थान पर इस्तेमाल किया गया था ताकि इसे टाल दिया जा सके और इसे और अधिक छोटी गाड़ी से बदल दिया जा सके। –

+2

'क्लोन' केवल कुछ मामलों में उपयोगी है जहां एक सही कार्यान्वयन पहले से मौजूद है। नए कोड में, 'क्लोन' का पर्दाफाश करने का कोई कारण नहीं है, यहां तक ​​कि छोटी कक्षाओं से भी नहीं; बस इसके बजाय एक प्रतिलिपि प्रदान करें। –

-3

एक तरीका है, कि विधि डेवलपर पैरामीटर को संशोधित करने वाला नहीं है, तो पैरामीटर को अंतिम रूप में चिह्नित करना चाहिए।

 public void test(final Object param) 

हालांकि बहुत कम लोग इसका अनुसरण करते हैं, इसलिए यह जानना मुश्किल है। हालांकि अच्छा प्रोग्रामर इस नियम का पालन करता है, खासकर एपीआई लिख रहा है। यदि आप विधि लिखना चाहते हैं और इसे बेनकाब करना चाहते हैं।यह इंगित करने के लिए परम अंतिम बनाएं कि पारित वस्तु को संशोधित नहीं किया जा रहा है।

+7

ऐसा करना एक बुरा विचार है क्योंकि ऐसा नहीं करता है जो ऐसा लगता है। यह * संदर्भ * पैरा को विधि के अंदर संशोधित करने से रोकता है, हालांकि यह मान द्वारा पारित किया जाता है, इसलिए कॉलर यह देखने में सक्षम नहीं होगा कि आपने इसे संशोधित किया है या नहीं। यह 'param' ऑब्जेक्ट को संशोधित होने से नहीं रोकेगा। –

+0

क्या यह जावाडोक में भी दिखता है? या प्रतिबिंब के माध्यम से? मुझे यकीन नहीं है कि अगर आप ऐसा करते हैं तो कोई कैसे जानता होगा। – user2357112

+1

@ पीटर, यह वास्तव में एक _recommended_ अभ्यास है, बुरा नहीं है क्योंकि यह किसी को यह स्पष्ट करता है कि आपको संदर्भ को पुन: असाइन नहीं करना चाहिए। आप सही हैं कि यह अपरिवर्तनीयता से भ्रमित नहीं होना चाहिए। अधिक चर्चा [इस धागे में] (http://stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java)। –

6

जावा साइड में एक साइड इफेक्ट विश्लेषण नहीं बनाया गया है।

आप मैन्युअल निरीक्षण के माध्यम से साइड इफेक्ट विश्लेषण कर सकते हैं, लेकिन प्रक्रिया को स्वचालित करने के लिए कई टूल मौजूद हैं।

आप कि क्या आपके कोड दुष्प्रभाव एक पैरामीटर पता लगाने के लिए एक अनुमान उपकरण (1, 2, 3) का उपयोग कर सकते हैं।

आप भी अपने कोड में पवित्रता या पक्ष प्रभाव एनोटेशन बारे में और फिर एक जाँच/सत्यापन उपकरण (1, 2) का उपयोग सुनिश्चित करें कि आपके कोड एनोटेशन लिखा है आप के अनुरूप कर सकते हैं।

उपर्युक्त सभी लिंक में सीमाएं हैं, लेकिन आप उन्हें उपयोगी पा सकते हैं। यदि आप अन्य उपकरणों के बारे में जानते हैं, तो टिप्पणियों में उनका उल्लेख करें।

8

नोट: इस उत्तर

You can also write purity or side-effect annotations in your code के एक अधिक विस्तृत संस्करण है - mernst

वहाँ विभिन्न चीजों यह एनोटेशन के माध्यम से संकलन समय पर जांच कर सकते हैं के बीच Checker Framework मौजूद नहीं है IJG Immutablity checker। यह चेकर आपको @Immutable या @ReadOnly के साथ ऑब्जेक्ट संदर्भों को एनोटेट करने की अनुमति देता है।

समस्या यह है कि आपको अक्सर annotate the library स्वयं करना होगा। अपने कार्य को कम करने के लिए चेकर फ्रेमवर्क एनोटेशन का हिस्सा automatically infer कर सकता है; आपको अभी भी बहुत कुछ करना होगा।

+1

'अपरिवर्तनीय' एनोटेशन ऑब्जेक्ट अपरिवर्तनीयता को लागू करता है। मूल पोस्टर संदर्भ अपरिवर्तनीयता के बारे में पूछ रहा था, जो '@ ReadOnly' एनोटेशन द्वारा प्रदान किया जाता है। – mernst

+2

[चेकर फ्रेमवर्क] (http://checkerframework.org/) की स्थिति के संबंध में, इसका उपयोग Google द्वारा हर दिन सैकड़ों परियोजनाओं पर किया जाता है। इसका उपयोग वॉल स्ट्रीट और कई अन्य व्यवसायियों और अकादमिकों द्वारा भी किया जाता है। यह एक आधिकारिक मानक नहीं है और कोई भी संबंधित जेएसआर नहीं है - यह सिर्फ एक उपयोगी उपकरण है। – mernst

3

मैं कैसे निर्धारित करूं कि बू द्वारा संदर्भित ऑब्जेक्ट फ़ंक्शन के शरीर (शायद एनोटेशन का उपयोग करके) में संशोधित किया गया है या नहीं?

मैं अन्य जवाब के साथ सहमत होना होगा कि विधि निर्धारित करने के लिए सुनिश्चित करें कि विधि संशोधित नहीं कर सकते बनाने के लिए अपनी वस्तु या नहीं और हाँ संशोधित करेगा कोई सीधा रास्ता है कि वहाँ अपने Object आप सभी यह अपनी तरफ से है क्या करना है ।

यदि कोई रास्ता नहीं है, तो क्या कुछ व्यापक रूप से उपयोग किए जाने वाले सम्मेलन या भ्रम और बग से बचने के लिए कुछ सर्वोत्तम प्रथाएं हैं?

यहाँ विधि नाम दृश्य की बात आती है। विधि के नामकरण सम्मेलन के साथ आगे बढ़ते हुए हमें कुछ विधि घोषणाओं पर एक नज़र डालना है जो स्पष्ट रूप से आपको विश्वास दिलाता है कि आपका Object बिल्कुल नहीं बदला जाएगा।

उदाहरण के लिए, तुम्हें पता है कि Arrays.copyOf अपने वास्तविक सरणी परिवर्तन नहीं होगा, System.out.println(boo) नहीं बदलेगा अपने boo

विधि नाम विधि उपयोगकर्ता के लिए जितना संभव हो उतना जानकारी प्रदान करने के वास्तविक हथियारों हैं। (हाँ! यह हमेशा संभव नहीं है लेकिन पालन करने के लिए काफी अच्छा अभ्यास है।)

की यह अपने मामले कहना है कि printBoo केवल प्रिंट, copyBoo कॉपी केवल जाएगा, clearBoo सभी विशेषताओं को रीसेट जाएगा में विचार करें, checkAndCreateNewBoo जाँच अपने booObject और यदि आवश्यक हो तो नए पैदा करेगा।

तो, आखिरकार यदि हम उन्हें उचित तरीके से उपयोग कर सकते हैं तो कॉलर को इस तथ्य से आश्वस्त किया जा सकता है कि विधि को कॉल करने के बाद Object वही रहेगा।

2

हर किसी को कहते हैं, अपरिवर्तनीय वस्तुओं का उपयोग करना पसंद है और यह भी इस

void foo(Boo boo) {...} 

तरह शून्य तरीकों

तरीकों में से उपलब्ध प्रयोजनों से बचने कर रहे हैं वस्तु ही की स्थिति बदलने या वस्तु को बदल पैरामीटर

void completOrder(Order order) { ... } 
//or 
void parserTokenEnded(String str) { ... } 
संबंधित मुद्दे