How do I prevent the modification of a private field in a class?

14,928

Solution 1

You must return a copy of your array.

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

Solution 2

If you can use a List instead of an array, Collections provides an unmodifiable list:

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

Solution 3

Modifier private protects only field itself from being accessed from other classes, but not the object references by this field. If you need to protect referenced object, just do not give it out. Change

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

to:

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

or to

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

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

Solution 4

The Collections.unmodifiableList has already been mentioned - the Arrays.asList() strangely not! My solution would also be to use the list from the outside and wrap the array as follows:

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

The problem with copying the array is: if you're doing it every time you access the code and the array is big, you'll create a lot of work for the garbage collector for sure. So the copy is a simple but really bad approach - I'd say "cheap", but memory-expensive! Especially when you're having more than just 2 elements.

If you look at the source code of Arrays.asList and Collections.unmodifiableList there is actually not much created. The first just wraps the array without copying it, the second just wraps the list, making changes to it unavailable.

Solution 5

You can also use ImmutableList which should be better than the standard unmodifiableList. The class is part of Guava libraries that was create by Google.

Here is the description:

Unlike Collections.unmodifiableList(java.util.List), which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change

Here is a simple example of how to use it:

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

  public ImmutableList<String> getArr() 
  {
    return ImmutableList.copyOf(arr);
  }
}
Share:
14,928

Related videos on Youtube

Hossein
Author by

Hossein

Updated on January 18, 2021

Comments

  • Hossein
    Hossein over 3 years

    Imagine that I have this class:

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

    Now, I have another class that uses the above class:

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

    So this is the problem: I have accessed a private field of a class from outside! How can I prevent this? I mean how can I make this array immutable? Does this mean that with every getter method you can work your way up to access the private field? (I don't want any libraries such as Guava. I just need to know the right way to do this).

    • OldCurmudgeon
      OldCurmudgeon over 11 years
      Actually, making it final does prevent the modification of the field. However, preventing the modification of the Object referred to by a field is more complicated.
    • Joren
      Joren over 11 years
      There's a problem with your mental model if you think being able to modify an array to which you have a reference stored in a private field is the same as being able to modify a private field.
    • Erik  Reppen
      Erik Reppen over 11 years
      If it's private, why expose it in the first place?
    • drobson
      drobson over 11 years
      Both OldCurmudgeon and sp00m anwsers are right. Which one to use depends on the situation. I would recommend you read Effective Java - Item 13: Minimize the accessibility of classes and members.
    • Bill K
      Bill K over 11 years
      It took me a while to figure this out but it always improves my code when I ensure that all data structures are completely encapsulated within their object--meaning there is no way to "Get" your "arr", instead do whever you must inside the class or provide an iterator.
    • Alvin Wong
      Alvin Wong over 11 years
      I propose editing the title to Prevent modification to a private array field?.
    • Qwerky
      Qwerky over 11 years
      I see nobody has mentioned Josh Bloch's Effective Java - Item 39: Make defensive copies when needed
    • Roman
      Roman about 11 years
      Is anyone else also surprised why this question got so much attention?
    • Maarten Bodewes
      Maarten Bodewes over 9 years
      I don't get the upvotes for Erik Reppen's comment. Data encapsulation doesn't mean you cannot expose internal state, but it should make sure that the instance controls the internal state (for instance by only returning read only values or copies of the values stored).
  • OldCurmudgeon
    OldCurmudgeon over 11 years
    I would like to remind people that although this has been voted the correct answer to the question, the best solution to the problem is actually as sp00m says - to return an Unmodifiable List.
  • Lemon
    Lemon over 11 years
    Not if you actually need to return an array.
  • Christoffer Hammarström
    Christoffer Hammarström over 11 years
    Actually, the best solution to the problem discussed is often as @MikhailVladimirov says: - to provide or return a view of the array or collection.
  • Konrad Rudolph
    Konrad Rudolph over 11 years
    @Svish And how often is that? Returning an array is almost certainly unnecessary.
  • jwenting
    jwenting over 11 years
    but do make sure it's a deep copy, not a shallow copy, of the data. For Strings it makes no difference, for other classes like Date where the content of the instance can be modified it can make a difference.
  • Lemon
    Lemon over 11 years
    @KonradRudolph Doesn't matter how often it is. Sometimes it is, and since the question involved one I find it appropriate that the answer does too :)
  • Konrad Rudolph
    Konrad Rudolph over 11 years
    @Svish I should’ve been more drastic: if you return an array from an API function you’re doing it wrong. In private functions inside a library it might be right. In an API (where protection against modifiability plays a role), it never is.
  • michael_s
    michael_s over 11 years
    I also say it's a bad solution - see my answer for clarification.
  • ruakh
    ruakh over 11 years
    @ChristofferHammarström: That's the same as sp00m's solution. Despite what the name unmodifiableList might sound like, it actually returns an unmodifiable view of a possibly-modifiable list.
  • Christoffer Hammarström
    Christoffer Hammarström over 11 years
    @ruakh: That is true, but i thought that solution was too specific. You don't have to return an unmodifiable view if you can provide one through other methods.
  • Xavi López
    Xavi López over 11 years
    @ruakh the unmodifiable view could actually be modifiable through reflection if there isn't a proper SecurityManager.
  • ruakh
    ruakh over 11 years
    @XaviLópez: So what? Even java.lang.String can be messed with via reflection. Remember: encapsulation is about protecting code from code, not about protecting code from coders.
  • Daniel Pryden
    Daniel Pryden over 11 years
    Related reading: Eric Lippert's article Arrays Considered Somewhat Harmful.
  • gyorgyabraham
    gyorgyabraham over 11 years
    Well Java has references, while C has pointers. Enough said. Further, the "final" modifier can have multiple impacts depending on where it is applied. Applying on a class member will enforce you to only assign value to the member exactly once with the "=" (assignment) operator. This has nothing to do with immutability. If you replace the reference of a member to a new one (some other instance from the same class), the previous instance will remain unchanged (but may be GCed if no other references are holding them).
  • Lyn Headley
    Lyn Headley about 11 years
    This article does not argue against using clone on arrays, only on objects that can be subclassed.
  • Scheintod
    Scheintod almost 10 years
    I would say michael_s answer makes the most sense.
  • Maarten Bodewes
    Maarten Bodewes almost 10 years
    You make the assumption here that the array including the strings is copied each time. It isn't, only the references to the immutable strings are copied. As long as your array is not huge, the overhead is negligible. If the array is huge, go for lists and immutableList all the way!
  • Maarten Bodewes
    Maarten Bodewes almost 10 years
    -1 as it doesn't do anything over calling clone, and is not as concise.
  • Maarten Bodewes
    Maarten Bodewes almost 10 years
    Added an answer that uses Collections.unmodifiableList(list) as assignment to the field, to make sure that the list isn't changed by the class itself.
  • Maarten Bodewes
    Maarten Bodewes almost 10 years
    Use this solution if you cannot trust the Test class to leave the returned list unaltered. You need to make sure that ImmutableList is returned, and that the elements of the list are immutable as well. Otherwise my solution should be safe as well.
  • michael_s
    michael_s almost 10 years
    No, I didn't make that assumption - where? It is about the references that are copied - doesn't matter if it is a String or any other object. Just saying that for big arrays I wouldn't recommend copying the array and that the Arrays.asList and Collections.unmodifiableList operations are probably cheaper for bigger arrays. Maybe someone can calculate how many elements are necessary, but I've already seen code in for-loops with arrays containing thousands of elements being copied just to prevent modification - that's insane!
  • Thomas15v
    Thomas15v over 9 years
    Just wondering, If you decompile this method you get a lot of new keywords. Doesn't this hurt the performance when getList() get access a lot. For example in render code.
  • Lii
    Lii over 9 years
    Note that this works if the elements of the arrays are immutable in themselves like String is, but if they are mutable something might have to be done to prevent the caller from changing them.