Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 229 posts at DZone. You can read more from them at their website. View Full User Profile

Back to Basics: Encapsulating Collections

06.16.2014
| 5659 views |
  • submit to reddit

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

Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Conrad Groth replied on Thu, 2014/06/19 - 8:30am

I agree with your statement, that handling with given collections or returning references to your internal collections break the encapsulation of your object. I would like to underline the words "starting point" in the implementation proposal paragraph, because there is (at least) one pitfall left behind:

The method Collections.unmodifiableList allows modules to provide users with "read-only" access to internal lists. If you are now, for example, iterating through the returned collection, while the "owner" of the collection deletes an element, you can get a ConcurrentModificationException (depending on what type of collection is used internally).

Kacper Skory replied on Thu, 2014/06/19 - 1:39pm in response to: Conrad Groth

That's right- I think ImmutableList from Guava is good solution for that.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.