Search code examples
javalistmutable

Trying to keep ArrayList Immutable


So I have a class named Album which contains a static ArrayList named 'listOfAllAlbumsCreated'.

   public class Album {
    private static ArrayList<Album> listOfAllAlbumsCreated= new ArrayList<>();

           public Album(String albumTitle) {
                this.albumTitle = albumTitle;
                listOfAllAlbumsCreated.add(this);

    }


public static ArrayList<Album> getListOfAllAlbumsCreated() {
        return listOfAllAlbumsCreated;
    }// I only want PlayList Class to Access this!

}

Basically, whenever an Album object is created, 'listOfAllAlbumsCreated' adds that object to it. Then, I created a PlayList Class which should have access to all albums created- by getting the 'listOfAllAlbumsCreated' and assigning another ArrayList to it.

 public class PlayList {
    private static ArrayList<Album> allExistingAlbums = Album.getListOfAllAlbumsCreated();

 public PlayList(String playListName) {
        this.playListName = playListName;
    }

}

This, however, allows anyone in other classes to just call the Album.getListOfAllAlbumsCreated and possibly change the content of all albums created which i obviously don't want to. So how can i keep listOfAllAlbumsCreated safe; i.e. only accessible to my PlayList class?

Thanks!


Solution

  • Yes, it is possible. There are two options that come to my mind.

    Option 1: limit access through access modifiers.

    The Oracle tutorial on access control has a neat table overview of what the different access modifiers do (Table generate with Senseful):

    ╔═════════════╦═══════╦═════════╦══════════╦═══════╗
    ║  Modifier   ║ Class ║ Package ║ Subclass ║ World ║
    ╠═════════════╬═══════╬═════════╬══════════╬═══════╣
    ║ public      ║ Y     ║ Y       ║ Y        ║ Y     ║
    ║ protected   ║ Y     ║ Y       ║ Y        ║ N     ║
    ║ no modifier ║ Y     ║ Y       ║ N        ║ N     ║
    ║ private     ║ Y     ║ N       ║ N        ║ N     ║
    ╚═════════════╩═══════╩═════════╩══════════╩═══════╝
    

    From your description, the private modifier (granting access only from within the current class), the no modifier (granting access only from within the current class and classes in the same package) or the protected modifier (granting acces from within the current class, classes in the same package and deriving classes) might be what you are looking for. The example shows it with no modifier.

    static ArrayList<Album> getListOfAllAlbumsCreated() {
        return listOfAllAlbumsCreated;
    }
    

    Option 2: Return an unmodifiable view of your list.

    To return an unmodifiable view of your List throung Collections.unmodifiableList(...). Your code would then look like this:

    public static ArrayList<Album> getListOfAllAlbumsCreated() {
        return Collections.unmodifiableList(listOfAllAlbumsCreated);
    }
    

    Two words of warning regarding the unmodifiable list:

    • changes made to the original List after you called getListOfallAlbumsCreated() will not be reflected in the unmodifiable view. The unmodifiable view is only a view of the list at the point in time you created the unmodifiable view.
    • While the returned List is unmodifiable, the entries within the List are not. Java has no concept of const objects, like C++ has. To achieve true immutability, you need to desing your Album class as immutable. In the future, we will most probably have value types, which will ease the implementation of immutables, coupled with some neat performance benefits.

    Two design remarks:

    • What you are doing is basically re-create the Java-equivalent to a memory leak. An instace of Album will not be garbage-collected unless you also remove it from listOfAllAlbumsCreated. This may or may not be what you want. What you can do is use WeakReferences, in order to allow garbage-collection of objects, if only WeakReferences to this object exist. You would change listOfAllAlbumsCreated to List<WeakReference<Album>>. This, in return, can lead to situations where some references return null when you call get() on them. You could try to solve this by overriding the finalize() method, but this is highly discouraged.

    • If you already have some kind of bookkeeping for your Albums, you could implement the Factory Method Pattern or the Builder Pattern in order to prevent creation of duplicates. For a more complete example of this, you may want to take a look at the implementation of Integer.valueOf(int value).