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

JavaArraysOopClass

Java Problem Overview


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).

Java Solutions


Solution 1 - Java

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

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

Solution 2 - Java

You must return a copy of your array.

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

Solution 3 - Java

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 - Java

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 - Java

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);
  }
}

Solution 6 - Java

at this point of view you should use system array copy:

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;
   }
}

Solution 7 - Java

You could return a copy of the data. The caller who chooses to change the data will only be changing the copy

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;
	}
}

Solution 8 - Java

The nub of the problem is that you are returning a pointer to a mutable object. Oops. Either you render the object immutable (the unmodifiable list solution) or you return a copy of the object.

As a general matter, finality of objects does not protect objects from being changed if they are mutable. These two problems are "kissing cousins."

Solution 9 - Java

Returning an unmodifiable list is a good idea. But a list that is made unmodifiable during the call to the getter method can still be changed by the class, or classes that are derived from the class.

Instead you should make it clear to anybody that extends the class that the list should not be modified.

So in your example it could lead to the following code:

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;
    }
}

In the above example I've made the STRINGS field public, in principle you could do away with the method call, as the values are already known.

You could also assign the strings to a private final List<String> field made unmodifiable during construction of the class instance. Using a constant or instantiation arguments (of the constructor) depends on the design of the class.

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;
    }
}

Solution 10 - Java

Yes, you should return a a copy of the array:

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

Solution 11 - Java

Since Java 9, an immutable list can also be constructed from a static factory method List.of() which results in just a bit fewer imports and code:

With an alias being returned from getUsers() when the original users fields can be modified:

class Computer {
    private String[] users = new String[] {"user1", "user2", "user3"};
    public String[] getUsers;

    String[] getUsers() {
        return this.users;
    }
}

Computer c = new Computer();
c.getUsers()[0] = "me";
for (String user: c.getUsers()) {
    System.out.println(user);
}

Output:

me
user2
user3

Using the immutable list:

import java.util.List;

class Computer {
    private String[] users = new String[] {"user1", "user2", "user3"};
    public List<String> getUsers;

    List<String> getUsers() {
        return List.of(this.users);
    }
}

Computer c = new Computer();
c.getUsers().set(0, "me");
for (String user: c.getUsers()) {
    System.out.println(user);
}

Output:

user1
user2
user3

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHosseinView Question on Stackoverflow
Solution 1 - Javasp00mView Answer on Stackoverflow
Solution 2 - JavaOldCurmudgeonView Answer on Stackoverflow
Solution 3 - JavaMikhail VladimirovView Answer on Stackoverflow
Solution 4 - Javamichael_sView Answer on Stackoverflow
Solution 5 - JavaiTechView Answer on Stackoverflow
Solution 6 - JavaG M RameshView Answer on Stackoverflow
Solution 7 - JavaanthonymsView Answer on Stackoverflow
Solution 8 - JavancmathsadistView Answer on Stackoverflow
Solution 9 - JavaMaarten BodewesView Answer on Stackoverflow
Solution 10 - JavakofemannView Answer on Stackoverflow
Solution 11 - JavaAlex TereshenkovView Answer on Stackoverflow