How do I prevent the modification of a private field in a class?
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);
}
}
Related videos on Youtube
Hossein
Updated on January 18, 2021Comments
-
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 over 11 yearsActually, making it
final
does prevent the modification of the field. However, preventing the modification of theObject
referred to by a field is more complicated. -
Joren over 11 yearsThere'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 over 11 yearsIf it's private, why expose it in the first place?
-
drobson over 11 yearsBoth 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 over 11 yearsIt 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 over 11 yearsI propose editing the title to
Prevent modification to a private array field?
. -
Qwerky over 11 yearsI see nobody has mentioned Josh Bloch's Effective Java -
Item 39: Make defensive copies when needed
-
Roman about 11 yearsIs anyone else also surprised why this question got so much attention?
-
Maarten Bodewes over 9 yearsI 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 over 11 yearsI 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 over 11 yearsNot if you actually need to return an array.
-
Christoffer Hammarström over 11 yearsActually, 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 over 11 years@Svish And how often is that? Returning an array is almost certainly unnecessary.
-
jwenting over 11 yearsbut 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 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 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 over 11 yearsI also say it's a bad solution - see my answer for clarification.
-
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 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 over 11 years@ruakh the unmodifiable view could actually be modifiable through reflection if there isn't a proper SecurityManager.
-
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 over 11 yearsRelated reading: Eric Lippert's article Arrays Considered Somewhat Harmful.
-
gyorgyabraham over 11 yearsWell 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 about 11 yearsThis article does not argue against using clone on arrays, only on objects that can be subclassed.
-
Scheintod almost 10 yearsI would say michael_s answer makes the most sense.
-
Maarten Bodewes almost 10 yearsYou 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 almost 10 years-1 as it doesn't do anything over calling
clone
, and is not as concise. -
Maarten Bodewes almost 10 yearsAdded 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 almost 10 yearsUse this solution if you cannot trust the
Test
class to leave the returned list unaltered. You need to make sure thatImmutableList
is returned, and that the elements of the list are immutable as well. Otherwise my solution should be safe as well. -
michael_s almost 10 yearsNo, 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
andCollections.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 over 9 yearsJust 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 over 9 yearsNote 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.