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:
- No reference to the collection is passed in the constructor, thus preventing any subsequent changes from outside the object
- Freedom to use the chosen collection implementation, with complete isolation - leaving room for change
- 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. |