Java – Good or Bad Practice to Mask Java Collections with Meaningful Class Names?

anti-patternscollectionsgenericsjava

Lately I've been in the habit of "masking" Java collections with human-friendly class names. Some simple examples:

// Facade class that makes code more readable and understandable.
public class WidgetCache extends Map<String, Widget> {
}

Or:

// If you saw a ArrayList<ArrayList<?>> being passed around in the code, would you
// run away screaming, or would you actually understand what it is and what
// it represents?
public class Changelist extends ArrayList<ArrayList<SomePOJO>> {
}

A colleague pointed out to me that this is bad practice, and introduces lag/latency, as well as being an OO anti-pattern. I can understand it introducing a very tiny degree of performance overhead, but can't imagine it's at all significant. So I ask: is this good or bad to do, and why?

Best Answer

Lag/Latency? I call BS on that. There should be exactly zero overhead from this practice. (Edit: It has been pointed out in the comments that this can, in fact, inhibit optimizations performed by the HotSpot VM. I don't know enough about VM implementation to confirm or deny this. I was basing my comment off of the C++ implementation of virtual functions.)

There is some code overhead. You have to create all the constructors from the base class that you want, forwarding their parameters.

I also don't see it as an anti-pattern, per se. However, I do see it as a missed opportunity. Instead of creating a class that derives the base class just for the sake of renaming, how about you instead create a class that contains the collection and offers a case-specific, improved interface? Should your widget cache really offer the full interface of a map? Or should it instead offer a specialized interface?

Furthermore, in the case of collections, the pattern simply doesn't work together with the general rule of using interfaces, not implementations - that is, in plain collection code, you would create a HashMap<String, Widget>, and then assign it to a variable of type Map<String, Widget>. Your WidgetCache cannot extend Map<String, Widget>, because that's an interface. It cannot be an interface that extends the base interface, because HashMap<String, Widget> doesn't implement that interface, and neither does any other standard collection. And while you can make it a class that extends HashMap<String, Widget>, you then have to declare the variables as WidgetCache or Map<String, Widget>, and the first loses you the flexibility to substitute a different collection (maybe some ORM's lazy loading collection), while the second kind of defeats the point of having the class.

Some of these counterpoints also apply to my proposed specialized class.

These are all points to consider. It may or may not be the right choice. In either case, your colleague's offered arguments are not valid. If he thinks it's an anti-pattern, he should name it.