An Architect's View

CFML, Clojure, Software Design, Frameworks and more...

An Architect's View

Caveat Container

November 7, 2008 ·

I said that I'd blog about a couple of painful lessons learned and that's a bit overdue. Sorry, I went to Holland for a week! However, here's one of those lessons learned... If you work with containers in Java (collection classes), you'll find that they have a contains(obj) method. The idea is that you can quickly check whether a given object is already in the collection by saying:
if (myCollection.contains(myObject)) ...
In many cases, you'll find this "just works" because contains() has the same idea of "equal" that you do for your objects. That is, it checks whether that exact same object is a member of the collection. Unfortunately, there are situations where "the same" object might be a separate instance that just happens to have all the same data (or at least the same key data). That can happen pretty easily when you are working with an ORM (like Transfer - although in our case it's Hibernate). You may be manipulating an object with, say, id = 783 and ask the ORM to load it from the database (or cache) so you can work on the "original" object. If the object you were working with has become disconnected from the ORM's cached version, you will now have two instances of the "same" object. They are "equal" as far as you are concerned but contains() will not consider them the same and you might be surprised to see it return false when you know the object is in the collection! This happened to me. My unit tests all passed but they had no disconnected objects. The real application began to fail and it took me a while to realize why. For me, objects are "equal" if their id properties are equal (and they are the same type of course). In my Groovy domain objects, I define equals() to specify this behavior and Groovy conveniently calls equals() when I say objA == objB. Operator overloading is very useful (and easy) in Groovy and it can help make your code much more readable. Unfortunately (for me), Java's collection classes don't respect that when contains() is called. Fortunately, Groovy provides a fairly simple workaround. You can use the find() method and a closure to specify the filter. find() returns the first matching object - or null if no objects match. myCollection.contains(myObject) can therefore be replaced with myCollection.find { it == myObject } or, if you want to be explicit, myCollection.find { obj -> obj == myObject } Since CFML does not allow you to compare objects directly, you're always going to have to write your own equality method. That means that any collection-based operations you do must explicitly call the appropriate equality method so, hopefully, you won't fall foul of Java's very literal "equality" test for the contains() method. If you work across both Java and CFML tho', you'll need to bear this in mind.

Tags: grails

0 responses