5

I use an ArrayList in one of my Java project's classes. The class keeps track of whether the list has been changed and offers public methods to add and remove elements from the list that automatically set the variable to "changed".

So far the list is public because I want my list to be publicly readable from everywhere. But I only want the class that owns the list to be able to modify it. So no modifications from outside classes. Is that possible? If so, how?

Usually for access control you'd probably use getter and setter methods. But even with a getter method and the list set to private another class could still do getList().remove(element) and thereby modify the list from the outside without the class noticing that the list was changed.

hannele
  • 53
  • 4
stefanbschneider
  • 5,460
  • 8
  • 50
  • 88
  • Provide all desired functionality only through public functions, completely encasulating the internal data structure. So write your own remove function. – SBI Dec 24 '14 at 10:49

5 Answers5

3

Make your ArrayList field private, but have your getter return Collections.unmodifiableList(list), which provides an unmodifiable view.

This will allow external code to treat it as a normal List, using for each loops and so on, but will disable modification operations. Additionally, unmodifiableList returns a view in constant time.

This is literally the exact use case it was designed for. Javadoc:

This method allows modules to provide users with "read-only" access to internal lists. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
2

Make your List private and add getter method:

public List getList(){
    return new ArrayList(yourPrivateList);
}
Tkachuk_Evgen
  • 1,334
  • 11
  • 17
1

You can make the ArrayList member private, and instead of a getter that returns the ArrayList, have a getter that accepts an index i and returns the i'th element of the ArrayList.

public class Test
{
    private List<String> list = new ArrayList<String>();

    public getString (int i)
    {
        // you might want to add some validation of i here
        return list.get(i);
    }

}

getString allows the users of your class to access any element of the list without being able to modify it.

If you want to allow your users to iterate over the list without being able to modify it, you can add a getSize() method to return the size of the list (which would allow the users to iterate over the list using the regular for loop), or your class can implement Iterable (without supporting the remove operation).

Eran
  • 387,369
  • 54
  • 702
  • 768
1

You have couple of options here:

  • The getter that returns the ArrayList can clone before returning the object. This way, even if the outside entity modifies the object, they'll end up modifying the clone - not your original object. Note: The clone operation can be costly. I'd suggest the below option.
  • Use Collections.unmodifiableList(..). Check the documentation here.
  • Or as other answers suggest: roll out your own methods for access and iteration.
UltraInstinct
  • 43,308
  • 12
  • 81
  • 104
1

I think your best option here is to keep your List private and add a getter method that returns a copy of the List, but not the List itself. For example:

public class EncapsulationTest {

    private List<Object> privateList = new ArrayList<Object>();

    // Your constructors and methods to track list
    // modification here ...

    public List<Object> getList() {
        // Maybe you need a null check here
        return new ArrayList<Object>(privateList);
    }

    public void addElement(Object newElement) {
        this.privateList.add(newElement);
        // Set your 'changed' variable to true
    }

    public void removeElement(Object element) {
        this.privateList.remove(element);
        // Set your 'changed' variable to true
    }
}

If you do this, you can still read an exact copy of the List, but you can't modify the List itself. Well, actually you can modify the returned List, but as it is a different object, the changes won't affect your object's List.

I hope it helps.

Alvaro Vazquez
  • 372
  • 3
  • 9
  • Thrustmaster mentioned in his answer that returning a clone is costly. I'm using the list in a heuristic where runtime is of upmost importance and iterations over these (large) lists are frequent. – stefanbschneider Dec 24 '14 at 11:28