2013-06-04 4 views
15

जावा में गेटटर विधियों का उपयोग करने के बारे में मेरा कोई प्रश्न है। मान लीजिए मैं इस वर्ग के लिए किया था:क्या गेटटर के परिणाम को संशोधित करने से ऑब्जेक्ट को प्रभावित होता है?

class Test { 
    private ArrayList<String> array = new ArrayList<String>(); 

    public ArrayList getArray() { 
     return this.array; 
    } 

    public void initArray() { 
     array.add("Test 1"); 
     array.add("Test 2"); 
    } 
} 

class Start { 
    public static void main(String args[]) { 
     initArray(); 
     getArray().remove(0); 
    } 
} 

मेरा प्रश्न है:

वास्तविक ArrayList वस्तु संशोधित किया जा चाहेंगे ("टेस्ट 1" यह से हटा दिया)? मुझे लगता है कि मैंने इसे स्थानों में देखा है, लेकिन मैंने सोचा कि गेटर्स बस उस वस्तु की एक प्रति प्रदान कर रहे थे। इसका संदर्भ नहीं है। यदि यह है कि जिस तरह से काम करते हैं (एक संदर्भ के रूप में) किया था, तो यह काम के साथ-साथ (वर्ग टेस्ट के ArrayList वस्तु इस द्वारा बदला जा सकते हैं और साथ ही) हैं ?:

class Start { 
    public static void main(String args[]) { 
     initArray(); 
     ArrayList aVar = getArray(); 
     aVar.remove(0); 
    } 
} 
+3

जावा कभी वस्तुओं की प्रतिलिपि नहीं करता है। – SLaks

+0

इस उदाहरण में एक गेटर एक वस्तु को संशोधित करता है? – raina77ow

+0

यह सत्यापित करना आसान लगता है। पदार्थ में केवल आपके संदर्भ में संदर्भ पारित किए जाते हैं ताकि संशोधन मूल सरणी को प्रभावित कर सकें। – assylias

उत्तर

21

जावा सरणी के लिए संदर्भ देता है, इसलिए यह एक प्रति नहीं होगी और यह सूची को संशोधित करेगा। आम तौर पर, जब तक कि यह एक आदिम प्रकार (int, float, आदि) आपको ऑब्जेक्ट का संदर्भ प्राप्त होगा।

यदि आप डुप्लिकेट वापस लौटना चाहते हैं तो आपको स्पष्ट रूप से सरणी को प्रतिलिपि बनाना होगा।

+2

यह एक सामान्य लेकिन संक्षिप्त उत्तर के लिए "उन प्रकारों पर रैपर" के मामले में भी एक संदर्भ है ... –

+0

+1। – MegaWidget

+0

ग्रेट। जवाब के लिए धन्यवाद। तो कोड का दूसरा भाग मूल वस्तु को भी संशोधित करेगा, सही? – Jordanss10

2

जिस तरह से मैं इसे समझता हूं, ऑब्जेक्ट रेफरेंस चर ऑब्जेक्ट्स के मेमोरी पतों से थोड़ा अधिक हैं। तो getArray() से वापस क्या किया जाता है उस ArrayList के लिए संदर्भ चर है। किसी ऑब्जेक्ट में कई संदर्भ चर हो सकते हैं, लेकिन यह अभी भी वही ऑब्जेक्ट है जो संशोधित हो जाता है।

जावा सब कुछ मूल्य से गुजरता है। इसलिए जब भी आप किसी ऑब्जेक्ट संदर्भ चर को पैरामीटर के रूप में पास करते हैं या उसके मान को वापस करते हैं, तो आप ऑब्जेक्ट संदर्भ चर के मान को पास या वापस कर रहे हैं।

0

अपने प्रश्न का उत्तर देने के लिए, गेटटर के साथ आपको एक चर के लिए सीधे पहुंच मिलती है। इस कोड को चलाएं और आप देख सकते हैं कि ArrayList में स्ट्रिंग हटा दी गई है। लेकिन इस कोड में अपने कोड में एक स्थिर ArraList का उपयोग न करें।

public class Test { 

     private static ArrayList<String> array = new ArrayList<String>(); 

     public static ArrayList<String> getArray() { 
      return array; 
     } 

     public static void initArray() { 
      array.add("Test 1"); 
      array.add("Test 2"); 
     } 

    public static void main(String[] args) { 
      initArray(); 
      ArrayList aVar = getArray(); 
      aVar.remove(0); 
      System.out.println(aVar.size()); 
    } 

} 
0

के रूप में बताया, अपने गेटर सूची को संशोधित नहीं है, यह रिटर्न सूची में एक परिवर्तनीय संदर्भ। FindBugs जैसे टूल के बारे में आपको चेतावनी देगा ... आप या तो उस के साथ रहते हैं और अपने वर्ग की उन पर भरोसा कर सकते हैं अपनी सूची पीटना नहीं करने के लिए, या इस का उपयोग अपनी सूची में एक unmodifiable संदर्भ वापस जाने के लिए:

public static List<String> getArray() { 
      return Collections.unmodifiableList(array); 
     } 
+0

तो इस मामले में, क्या यह पास-दर-संदर्भ या पास-दर-मान माना जाएगा? और जब आप कहते हैं कि "आपका गेटटर सूची को संशोधित नहीं करता है" लेकिन "यह सूची में एक संशोधित संदर्भ देता है", क्या इसका मतलब है कि getArray() DO के संदर्भ में किए गए परिवर्तन सरणी सूची ऑब्जेक्ट पर प्रभाव डालते हैं टेस्ट क्लास? – Jordanss10

0

कि एक गेटर उस ऑब्जेक्ट को संशोधित नहीं करता है जिसे आप इसे कहते हैं, पूरी तरह से सम्मेलन का विषय है। यह निश्चित रूप से लक्ष्य की पहचान को नहीं बदलता है, लेकिन यह अपनी आंतरिक स्थिति बदल सकता है। यहाँ एक उपयोगी उदाहरण है, अगर थोड़ा अधूरा:

public class Fibonacci { 
    private static ConcurrentMap<Integer, BigInteger> cache = 
     new ConcurrentHashMap<>(); 

    public BigInteger fibonacci(int i) { 
     if (cache.containsKey(i)) { 
      return cache.get(i); 
     } else { 
      BigInteger fib = compute(i); // not included here. 
      cache.putIfAbsent(i, fib); 
      return fib; 
    } 
} 

तो, बुला Fibonacci.fibonacci(1000) लक्ष्य की आंतरिक स्थिति को बदल सकते हैं, लेकिन यह अभी भी एक ही लक्ष्य है।

public class DateRange { 
    private Date start; 
    private Date end; 
    public DateRange(final Date start, final Date end) { 
     if (start.after(end)) { 
      throw new IllegalArgumentException("Range out of order"); 
     } 
     this.start = start; 
     this.end = end; 
    } 
    public Date getStart() { 
     return start; 
    } 
    // similar for setStart, getEnd, setEnd. 
} 

समस्या यह है कि java.lang.Date परिवर्तनशील है:

अब, यहाँ एक संभावित सुरक्षा उल्लंघन है। कोई कोड लिख सकता है जैसे:

DateRange range = new DateRange(today, tomorrow); 
// In another routine. 
Date start = range.getStart(); 
start.setYear(2088); // Deprecated, I know. So? 

अब सीमा क्रम से बाहर है। यह कैशियर को अपना वॉलेट सौंपने जैसा है।

यही कारण है कि इनमें से एक को करना सबसे अच्छा है, पहले वाले लोग बेहतर हैं।

  1. जितना संभव हो उतना ऑब्जेक्ट अपरिवर्तनीय हो। यही कारण है कि जोडा-टाइम लिखा गया था, और जावा 8 में अभी तक क्यों तारीखें चलेगी।
  2. आइटमों की रक्षात्मक प्रतियां एक सेट या हो जाती हैं।
  3. किसी आइटम का एक अपरिवर्तनीय रैपर लौटाएं।
  4. पुनरावृत्तियों के रूप में संग्रह संग्रह, स्वयं के रूप में नहीं। बेशक, कोई इसे वापस ला सकता है।
  5. आइटम तक पहुंचने के लिए प्रॉक्सी लौटें, जिसे इसके प्रकार पर नहीं डाला जा सकता है।

मुझे पता है, मुझे पता है। अगर मैं सी या सी ++ चाहता हूं, तो मुझे पता है कि उन्हें कहां मिलना है। 1. वापसी

1

जैसा कि अन्य ने कहा, जब तक कि यह एक आदिम प्रकार न हो, आपको वस्तु का संदर्भ मिलता है। यह सी ++ में पॉइंटर के समान है, यह आपको ऑब्जेक्ट तक पहुंचने की अनुमति देता है, लेकिन सी ++ संदर्भ (एक चर के मेमोरी एड्रेस पर पॉइंटर) के विपरीत यह आपको किसी अन्य ऑब्जेक्ट से प्रतिस्थापित करने की अनुमति नहीं देता है। केवल सेटटर ही ऐसा कर सकता है।

मुझे आपके प्रश्न में दो प्रकार दिखाई देते हैं, test.getArray().remove(0) और aVar.remove(0)। उन परिणामों के परिणाम में कोई अंतर नहीं है, यह अभी भी कुछ सूचक-जैसा संदर्भ है और यह मूल को संशोधित करता है।

आपको कभी भी गेटर को कॉल करके क्लोन नहीं मिलता है, इसलिए जब तक ऑब्जेक्ट अपरिवर्तनीय न हो, तो आप उस ऑब्जेक्ट को संशोधित कर सकते हैं जिस पर गेटटर ने आपको पहुंच प्रदान की है। उदाहरण के लिए, String अपरिवर्तनीय है, कोई भी मूल Collection (ArrayList समेत) उत्परिवर्तनीय है। संग्रह को अपरिवर्तनीय बनाने के लिए आप Collections.unmodifiable*(...) पर कॉल कर सकते हैं। हालांकि, अगर संग्रह की वस्तुएं उत्परिवर्तनीय हैं, तो भी वे बदल सकते हैं।

कुछ मामलों में, क्लोन प्राप्त करना एक अच्छा विचार है, ज्यादातर मामलों में यह नहीं है। एक गेटर को कुछ भी क्लोन नहीं करना चाहिए, इसे डेटा को तब तक संशोधित नहीं करना चाहिए जब तक कि यह संभावित रूप से शून्य संग्रह या ऐसा कुछ प्रारंभ नहीं करता है। यदि आप अपरिवर्तनीय संग्रह वाले अपरिवर्तनीय संग्रह चाहते हैं, तो इसे इस तरह से करने का प्रयास करें। इस उदाहरण में हमारे पास कक्षा FooImpl है जो बाद में समझाए जाने के कारण इंटरफ़ेस Foo लागू करता है।

public interface Foo { 
    int getBar(); 
} 

public class FooImpl Foo { 
    private int bar; 
    @Override 
    public int getBar() { 
     return bar; 
    } 
    public void setBar(int newValue) { 
     this.bar = newValue; 
    } 
} 

जैसा कि आप देख, फू कोई सेटर है। यदि आप कुछ ArrayList<Foo> बनाते हैं और इसे Collections.unmodifiableList(myArrayList) के रूप में कुछ गेटर से पास करते हैं, तो ऐसा लगता है कि आपने ऐसा किया है। लेकिन काम अभी तक नहीं किया गया है। यदि कक्षा FooImpl सार्वजनिक है (जो इस मामले में है), तो कोई भी कोशिश कर सकता है कि वह foo सूची में पाया गया है, वह instanceof FooImpl है और फिर इसे (FooImpl) foo के रूप में इसे परिवर्तनीय बनाते हैं। हालांकि, हम किसी भी Foo को FooWrapper नामक रैपर में लपेट सकते हैं। यह रूप में अच्छी तरह Foo लागू करता है:

public class FooWrapper implements Foo { 
    private Foo foo; 
    public FooWrapper(Foo foo) { 
     this.foo = foo; 
    } 
    public int getBar() { 
     return foo.getBar(); 
    } 
    // No setter included. 
} 

फिर हम एक Collection<FooWrapper> में एक new FooWrapper(myFoo) डाल सकते हैं। इस रैपर में कोई सार्वजनिक सेटटर नहीं है और अंदर फू निजी है। आप अंतर्निहित डेटा को संशोधित नहीं कर सकते हैं। अब उस Foo इंटरफ़ेस के बारे में। FooImpl और FooWrapper दोनों इसे कार्यान्वित करते हैं, यदि कोई विधि डेटा को संशोधित करने का इरादा नहीं रखती है, तो यह इनपुट पर Foo मांग सकता है। इससे कोई फर्क नहीं पड़ता कि आपको Foo मिलते हैं।

इसलिए, यदि आप unmodifiable unmodifiable डेटा वाली संग्रह चाहते हैं, एक नया Collection<Foo> बनाने FooWrapper वस्तुओं के साथ यह फ़ीड और फिर Collections.unmodifiable*(theCollection) कहते हैं। या कि फू के पूरे संग्रह इस सूची लपेटता, FooWrappers लौटने, उदाहरण के लिए, एक कस्टम संकलन करते हैं:

public MyUnmodifiableArrayList implements List<Foo> { 
    ArrayList<Foo> innerList; 
    public get(int index) { 
     Foo result = innerList.get(index); 
     if (!(result instanceof FooWrapper)) { 
      return new FooWrapper(result); 
     } 
     return result; // already wrapped 
    } 
    // ... some more List interface's methods to be implemented 
} 

लिपटे संग्रह के साथ, आप मूल संग्रह के माध्यम से पुनरावृति और के रैपर के साथ अपने क्लोन बनाने के लिए की जरूरत नहीं है डेटा। जब आप इसे पूरी तरह से नहीं पढ़ते हैं तो यह समाधान बहुत बेहतर होता है, लेकिन जब भी आप get() पर कॉल करते हैं, तब तक यह एक नया FooWrapper बनाता है, जब तक कि उस अनुक्रमणिका पर फ़ू पहले से ही FooWrapper नहीं है। लाखों कॉलों के साथ get() पर लंबे समय तक चलने वाले धागे में, यह कचरा कलेक्टर के लिए एक अनावश्यक बेंचमार्क बन सकता है, जिससे आप कुछ आंतरिक सरणी या मानचित्र का उपयोग कर सकते हैं जिसमें पहले से मौजूद FooWrappers शामिल हैं।

अब आप नया, कस्टम List<Foo> वापस कर सकते हैं। लेकिन फिर, एक सादा गेटर से नहीं। इसे अपने private ArrayList<FooImpl> fooList फ़ील्ड के लिए getUnmodifiableFooList() जैसे कुछ बनाएं।

संबंधित मुद्दे