I've been working on a project were I need to iterate over a set of objects, get some property from all objects in the collection, and store that property in a new collection. This wouldn't be much if an issue if I did it in LISP or Ruby or some think similar... but this is done in Java with th the
java.util.Collection framework. The Collection framework is nice and all, but when I have to write:
List<SomeProperty> allProperties(List<SomeClass> objects) {
List<SomeProperty> properties = new ArrayList<SomeProperty>();
for (SomeClass c : objects)
properties.add(c.getProperty());
return properties;
}when all I really wish to say is:
(defun all-propreties (objects)
(mapcar get-property objects))I get a bit sad. Sure computer technology is progressing, but are computer languages? LISP appeared in the late 50-ies and Java in the mid 90-ties, but looking at the code above I can't really tell that there is almost 40 years between these two languages. Crazy...
Anyway, to make me a bit happier I started to think about how the Java code above could be improved to make it a little bit more terse. What I came up with was this:
List<SomeProperty> allProperties(Bag<SomeClass> objects) {
return objects.collect(objects.onAll().getProperty());
}which I honestly think is pretty neat. Then again, I'm just an ape-descended life form who are so amazingly primitive that I still think digital watches are a pretty neat idea too.
How does this work? Well, first of all the
objects variable is not the same type in the two Java examples above. In the latter example, it is a class that I've implemented specially to deal with the scenario described. This class, which is called
Bag<T>, has a method
onAll(T) that returns an
dynamic proxy implementing the
T interface. That is, in the example above the
objects.onAll() returns an instance of the
SomeProperty interface.
This dynamic proxy handles every method call it receives by calling that method on each object in the
Bag. Also, if the called method is non-void, then it returns whatever the last object in the
Bag returned. This means that
bag.onAll().someMethod() behaves just as
thing.someMethod() if
bag contains just the
thing objects. This is a Good Thing in my book. :)
How about the Bag.collect method?, you ask.
Funny you should ask that. I was just getting to that, I reply.
Cut to the chase already!, you say.
Cool it, I say,
or there won't be any desert!.
(Ok, I'm getting a bit carried away.)
The
Bag.collect simply returns the set of return values got when last calling a method on the
onAll-object. Ehm, it a bit hard explain with words... but I think you understand. If you don't, look at the
code and
test-cases. :)
Note that the
onAll method returns an object that can be used for more than what's described above. Whenever you need to treat a set of objects as if they were one object, for instance the
Observer pattern, the
onAll-object simplifies alot.