मैं एक कक्षा में एक निजी क्षेत्र के संशोधन को कैसे रोकूं?


165

कल्पना कीजिए कि मेरे पास यह वर्ग है:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public String[] getArr() 
  {
    return arr;
  }
}

अब, मेरे पास एक और वर्ग है जो उपरोक्त वर्ग का उपयोग करता है:

Test test = new Test();
test.getArr()[0] ="some value!"; //!!!

तो यह समस्या है: मैंने बाहर से एक वर्ग के निजी क्षेत्र तक पहुंच बनाई है! मेरे द्वारा इसे कैसे रोका जा सकता है? मेरा मतलब है कि मैं इस सरणी को कैसे अपरिवर्तनीय बना सकता हूं? क्या इसका मतलब यह है कि प्रत्येक गेट्टर विधि से आप निजी क्षेत्र तक पहुंचने के लिए अपना काम कर सकते हैं? (मुझे अमरूद जैसी कोई लाइब्रेरी नहीं चाहिए। मुझे बस इसे करने का सही तरीका पता होना चाहिए)।


10
असल में, यह finalकरता है क्षेत्र के संशोधन को रोकने के । हालाँकि, Objectकिसी फ़ील्ड द्वारा संदर्भित के संशोधन को रोकना अधिक जटिल है।
OldCurmudgeon

22
आपके मानसिक मॉडल के साथ एक समस्या है अगर आपको लगता है कि आप एक सरणी को संशोधित करने में सक्षम हैं, जिसके लिए आपके पास एक निजी क्षेत्र में संग्रहीत एक संदर्भ है जो निजी क्षेत्र को संशोधित करने में सक्षम है।
जोरेन

45
यदि यह निजी है, तो इसे पहली जगह में क्यों उजागर करें?
एरिक रिपेन

7
मुझे यह पता लगाने में थोड़ा समय लगा, लेकिन यह हमेशा मेरे कोड में सुधार करता है जब मैं यह सुनिश्चित करता हूं कि सभी डेटा संरचनाएं पूरी तरह से अपनी वस्तु के भीतर समाविष्ट हैं - जिसका अर्थ है कि "अपना" गिरफ्तार "" करने के लिए कोई रास्ता नहीं है, इसके बजाय आपको घर के अंदर करना होगा कक्षा या एक पुनरावृत्ति प्रदान करते हैं।
बिल के

8
क्या किसी और को भी आश्चर्य हुआ कि इस प्रश्न पर इतना ध्यान क्यों गया?
रोमन

जवाबों:


163

आपको अपने सरणी की एक प्रति लौटानी होगी ।

public String[] getArr() {
  return arr == null ? null : Arrays.copyOf(arr, arr.length);
}

121
मैं लोगों को याद दिलाना चाहूंगा कि यद्यपि इस प्रश्न का सही उत्तर दिया गया है, समस्या का सबसे अच्छा समाधान वास्तव में है क्योंकि sp00m कहता है - वापस लौटने के लिए Unmodifiable List
OldCurmudgeon

48
नहीं अगर आपको वास्तव में एक सरणी वापस करने की आवश्यकता है।
शविश

8
दरअसल, चर्चा की गई समस्या का सबसे अच्छा समाधान अक्सर @MikhailVladimirov कहता है: - प्रदान करने या वापस करने के लिए सरणी या संग्रह का दृश्य
क्रिस्टोफर हम्मरस्ट्रॉम

20
लेकिन यह सुनिश्चित करें कि यह डेटा की उथली प्रति नहीं, एक गहरी प्रति है। स्ट्रिंग्स के लिए इससे कोई अंतर नहीं पड़ता है, अन्य वर्गों जैसे दिनांक के लिए जहां उदाहरण की सामग्री को संशोधित किया जा सकता है, इससे अंतर हो सकता है।
jwenting

16
अगर आप एक एपीआई समारोह से एक सरणी वापस कर रहे हैं तो आप इसे गलत कर रहे हैं: @ सविश मैं और अधिक कठोर होना चाहिए था। एक पुस्तकालय के अंदर निजी कार्यों में यह सही हो सकता है। एक एपीआई में (जहां मॉडिफ़िबिलिटी के खिलाफ सुरक्षा एक भूमिका निभाती है), यह कभी नहीं होता है।
कोनराड रुडोल्फ

377

यदि आप किसी सरणी के बजाय एक सूची का उपयोग कर सकते हैं, तो संग्रह एक अपरिवर्तनीय सूची प्रदान करता है :

public List<String> getList() {
    return Collections.unmodifiableList(list);
}

Collections.unmodifiableList(list)फ़ील्ड के असाइनमेंट के रूप में उपयोग किए जाने वाले उत्तर को यह सुनिश्चित करने के लिए जोड़ा गया कि सूची स्वयं वर्ग द्वारा परिवर्तित नहीं की गई है।
मार्टन बॉड्यूज

बस सोच रहा था, यदि आप इस विधि को अपघटित करते हैं तो आपको बहुत सारे नए कीवर्ड मिलते हैं। जब गेटलिस्ट () को बहुत अधिक एक्सेस मिले तो यह प्रदर्शन को नुकसान नहीं पहुंचाता है। उदाहरण के लिए रेंडर कोड में।
थॉमस

2
ध्यान दें कि यह काम करता है यदि सरणियों के तत्व अपने आप में अपरिवर्तनीय हैं String, लेकिन यदि वे परस्पर परिवर्तन करते हैं तो कॉलर को उन्हें बदलने से रोकने के लिए कुछ करना पड़ सकता है।
Lii

45

संशोधक privateकेवल फ़ील्ड को अन्य वर्गों से एक्सेस होने से बचाता है, लेकिन इस फ़ील्ड द्वारा ऑब्जेक्ट संदर्भ नहीं। यदि आपको संदर्भित ऑब्जेक्ट को संरक्षित करने की आवश्यकता है, तो बस इसे बाहर न करें। परिवर्तन

public String [] getArr ()
{
    return arr;
}

सेवा:

public String [] getArr ()
{
    return arr.clone ();
}

या इसमें

public int getArrLength ()
{
    return arr.length;
}

public String getArrElementAt (int index)
{
    return arr [index];
}

5
यह आलेख सरणियों पर क्लोन का उपयोग करने के खिलाफ बहस नहीं करता है, केवल उन वस्तुओं पर जिन्हें उपवर्गित किया जा सकता है।
लिन हेडली

28

Collections.unmodifiableListपहले ही उल्लेख किया गया है - Arrays.asList()अजीब नहीं! मेरा समाधान यह भी होगा कि सूची का उपयोग बाहर से किया जाए और सरणी को निम्न प्रकार से लपेटें:

String[] arr = new String[]{"1", "2"}; 
public List<String> getList() {
    return Collections.unmodifiableList(Arrays.asList(arr));
}

सरणी की प्रतिलिपि बनाने में समस्या यह है: यदि आप इसे हर बार कर रहे हैं जब आप कोड तक पहुंचते हैं और सरणी बड़ी होती है, तो आप कचरा संग्रहकर्ता के लिए बहुत सारे काम सुनिश्चित करेंगे। तो प्रतिलिपि एक सरल लेकिन वास्तव में बुरा दृष्टिकोण है - मैं कहूंगा "सस्ता", लेकिन स्मृति-महंगा! खासकर जब आप केवल 2 तत्वों से अधिक कर रहे हैं।

यदि आप के स्रोत कोड को देखते हैं Arrays.asListऔर Collections.unmodifiableListवास्तव में बहुत अधिक नहीं है। पहला सिर्फ इसे कॉपी किए बिना सरणी को लपेटता है, दूसरा सिर्फ सूची को लपेटता है, जिससे यह अनुपलब्ध है।


आप यहां यह धारणा बनाते हैं कि तार सहित सरणी को हर बार कॉपी किया जाता है। यह नहीं है, केवल अपरिवर्तनीय तार के संदर्भों की नकल की जाती है। जब तक आपका सरणी विशाल नहीं है, तब तक ओवरहेड नगण्य है। यदि सरणी विशाल है, तो सूचियों और immutableListसभी तरह से जाएं!
१०:१० में मैर्टन बोडेव्स

नहीं, मैंने वह धारणा नहीं बनाई - कहाँ? यह उन संदर्भों के बारे में है जो कॉपी किए गए हैं - कोई फर्क नहीं पड़ता कि यह स्ट्रिंग या कोई अन्य ऑब्जेक्ट है। सिर्फ इतना कहना है कि बड़े सरणियों के लिए मैं सरणी की प्रतिलिपि बनाने की सिफारिश नहीं करूंगा और यह कि बड़े सरणियों के लिए Arrays.asListऔर Collections.unmodifiableListसंचालन शायद सस्ता है। हो सकता है कि कोई यह गणना कर सके कि कितने तत्व आवश्यक हैं, लेकिन मैंने पहले से ही कोड को लूप में कोड के साथ देखा है जिसमें हजारों तत्वों को कॉपी किया जा रहा है ताकि संशोधन को रोका जा सके - यह पागल है!
michael_s

6

आप भी उपयोग कर सकते हैं ImmutableListजो मानक से बेहतर होना चाहिए unmodifiableList। वर्ग अमरूद का हिस्सा है Google द्वारा बनाई गई पुस्तकालयों ।

यहाँ वर्णन है:

संग्रहों के विपरीत ।unmodifiableList (java.util.List), जो एक अलग संग्रह का दृश्य है जो अभी भी बदल सकता है, ImmutableList के एक उदाहरण में अपना निजी डेटा होता है और यह कभी नहीं बदलेगा

इसका उपयोग करने का एक सरल उदाहरण यहां दिया गया है:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public ImmutableList<String> getArr() 
  {
    return ImmutableList.copyOf(arr);
  }
}

इस समाधान का उपयोग करें यदि आप क्लास को लौटाए गए सूची को अनछुए हुए पर भरोसा नहींTest कर सकते हैं । आपको यह सुनिश्चित करने की आवश्यकता है कि ImmutableListलौटा दिया गया है, और यह कि सूची के तत्व भी अपरिवर्तनीय हैं। अन्यथा मेरा समाधान भी सुरक्षित होना चाहिए।
मार्टन बॉड्यूज

3

इस दृष्टिकोण से आपको सिस्टम ऐरे कॉपी का उपयोग करना चाहिए:

public String[] getArr() {
   if (arr != null) {
      String[] arrcpy = new String[arr.length];
      System.arraycopy(arr, 0, arrcpy, 0, arr.length);
      return arrcpy;
   } else
      return null;
   }
}

-1 के रूप में यह कॉलिंग पर कुछ भी नहीं करता है clone, और संक्षिप्त रूप में नहीं है।
मार्टन बोडेवेस

2

आप डेटा की एक प्रति वापस कर सकते हैं। जो कॉलर डेटा बदलने का विकल्प चुनता है, वह केवल कॉपी बदल रहा होगा

public class Test {
    private static String[] arr = new String[] { "1", "2" };

    public String[] getArr() {

        String[] b = new String[arr.length];

        System.arraycopy(arr, 0, b, 0, arr.length);

        return b;
    }
}

2

समस्या का उत्तर यह है कि आप एक सूचक को एक परिवर्तनशील वस्तु पर वापस कर रहे हैं। उफ़। या तो आप ऑब्जेक्ट को अपरिवर्तनीय (अयोग्य सूची समाधान) को प्रस्तुत करते हैं या आप ऑब्जेक्ट की एक प्रति वापस करते हैं।

एक सामान्य बात के रूप में, वस्तुओं की अंतिमता वस्तुओं को बदलने से बचाती नहीं है यदि वे परिवर्तनशील हैं। इन दोनों समस्याओं "चचेरे भाई चुंबन।" कर रहे हैं


खैर जावा में संदर्भ हैं, जबकि सी में संकेत हैं। पर्याप्त कथन। इसके अलावा, "अंतिम" संशोधक के लागू होने के आधार पर कई प्रभाव हो सकते हैं। एक वर्ग के सदस्य पर लागू करना आपको केवल "=" (असाइनमेंट) ऑपरेटर के साथ सदस्य को केवल एक बार मान प्रदान करने के लिए लागू करेगा। इसका अपरिवर्तनीयता से कोई लेना-देना नहीं है। यदि आप एक सदस्य के संदर्भ को एक नए (उसी वर्ग से कुछ अन्य उदाहरण) को प्रतिस्थापित करते हैं, तो पिछला उदाहरण अपरिवर्तित रहेगा (लेकिन यदि कोई अन्य संदर्भ उन्हें नहीं पकड़ रहा है तो GCed हो सकता है)।
ग्योराभिराम

1

एक unmodifiable सूची लौटना एक अच्छा विचार है। लेकिन एक सूची जिसे कॉल्टर विधि के लिए कॉल के दौरान अपरिवर्तनीय बनाया जाता है, उसे अभी भी वर्ग, या वर्ग से ली गई कक्षाओं द्वारा बदला जा सकता है।

इसके बजाय आपको किसी को भी यह स्पष्ट करना चाहिए कि कक्षा का विस्तार हो कि सूची को संशोधित न किया जाए।

तो आपके उदाहरण में यह निम्नलिखित कोड तक ले जा सकता है:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    public static final List<String> STRINGS =
        Collections.unmodifiableList(
            Arrays.asList("1", "2"));

    public final List<String> getStrings() {
        return STRINGS;
    }
}

उपरोक्त उदाहरण में मैंने बनाया है STRINGS क्षेत्र को सार्वजनिक , सिद्धांत रूप में आप विधि कॉल के साथ दूर कर सकते हैं, क्योंकि मान पहले से ही ज्ञात हैं।

आप private final List<String>क्लास इंस्टेंस के निर्माण के दौरान स्ट्रिंग को एक ऐसे क्षेत्र में असाइन कर सकते हैं, जो असंबद्ध बनाया गया हो। एक निरंतर या तात्कालिक तर्क का उपयोग करना (निर्माता का) वर्ग के डिजाइन पर निर्भर करता है।

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    private final List<String> strings;

    public Test(final String ... strings) {
        this.strings = Collections.unmodifiableList(Arrays
                .asList(strings));
    }

    public final List<String> getStrings() {
        return strings;
    }
}

0

हाँ, आपको सरणी की एक प्रति वापस आनी चाहिए:

 public String[] getArr()
 {
    return Arrays.copyOf(arr);
 }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.