/ COLLECTIONS

Back to basics: encapsulating collections

Younger, I learned there were 3 properties of the Object-Oriented paradigm:

  • Encapsulation
  • Inheritance
  • Polymorphism

In Java, encapsulation is implemented through usage of private attributes with accessors methods commonly known as getters and setters. Whether this is proper encapsulation is subject to debate and is outside the scope of this article. However, using this method to attain encapsulation when the attribute is a collection (of types java.util.Collection, java.util.Map and their subtypes) is just plain wrong.

The code I see most of the times is the following:

public class MyBean {

    private Collection collection;

    public Collection getCollection() {
        return collection;
    }

    public void setCollection(Collection collection) {
        this.collection = collection;
    }
}

This is the most common code I see: this design has been popularized by ORM frameworks such as Hibernate. Many times when I raise this point, the next proposal is an immutable one.

public class MyBean {

    private Collection collection;

    public MyBean(Collection collection) {
        this.collection = collection;
    }

    public Collection getCollection() {
        return collection;
    }
}

No proper encapsulation

However, in the case of collections, this changes nothing as Java collections are mutable themselves. Obviously, both passing a reference to the collection in the constructor and returning a reference to it is no encapsulation at all. Real encapsulation is only possible if no reference to the collection is kept nor returned.

List list = new ArrayList();
MyBean mybean = new MyBean(list);
list.add(new Object()); // We just modified the collection outside my bean

Not possible to use a specific subtype

Besides, my bean could require a more specific collection of its own, such as List or Set. With the following code snippet, passing a Set is simply not possible.

public class MyBean {

    private List collection;

    public List getCollection() {
        return collection;
    }

    public void setCollection(List collection) {
        this.collection = collection;
    }
}

No choice of the concrete implementation

As a corollary from the last point, using the provided reference prevents us from using our own (perhaps more efficient) type e.g. a Apache Commons FastArrayList.

An implementation proposal

The starting point of any true encapsulation is the following:

public class MyBean {

    private List collection = new ArrayList();

    public MyBean(Collection collection) {
        this.collection.addAll(collection);
    }

    public Collection getCollection() {
        return Collections.unmodifiableList(collection);
    }
}

This fixes the aforementioned cons:

  1. No reference to the collection is passed in the constructor, thus preventing any subsequent changes from outside the object
  2. Freedom to use the chosen collection implementation, with complete isolation - leaving room for change
  3. No reference to the wrapped collection is passed in the collection returned by the getter
Previous snippets do not use generics for easier readability, please do use them.
Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a technologist focusing on cloud-native technologies, DevOps, CI/CD pipelines, and system observability. His focus revolves around creating technical content, delivering talks, and engaging with developer communities to promote the adoption of modern software practices. With a strong background in software, he has worked extensively with the JVM, applying his expertise across various industries. In addition to his technical work, he is the author of several books and regularly shares insights through his blog and open-source contributions.

Read More
Back to basics: encapsulating collections
Share this