Home > Java > Back to basics: encapsulating 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

Note: previous snippets do not use generics for easier readability, please do use them

email
Send to Kindle
Categories: Java Tags:
  1. Mike S
    June 15th, 2014 at 17:51 | #1

    Guava’s ImmutableList.copyOf(Iterable) can help. If you have a private final List then you can accept Iterable and initialize your field to ImmutableList.copyOf(input) and you get a guaranteed immutable snapshot of the input. The function is a no-op if the input is already an ImmutableList. You can then change your getter to return ImmutableList, thus advertising to clients that they should not attempt to mutate it.

  2. Reemi
    June 15th, 2014 at 20:37 | #2

    would this also be true for C#?

  3. Alexis
    June 16th, 2014 at 17:42 | #3

    Hello,

    I think we could discuss if the last MyBean is well “encapsulated”. I believe objects should be defined by I/O effects and behavior (more or less, the implemented responsability). If your objects are merely data containers (not even data structures a la Collections) and you only have methods that give data states to the outside world, you are still breaking encapsulation.

    You’ve reached Mutability on your last example, but yet that class has a lot to do before being promoted to Object :).

  4. Alexis
    June 16th, 2014 at 17:45 | #4

    @Alexis
    Probably since you can modify element inside the collection, you aren’t yet immutable.

  1. No trackbacks yet.