While I've been formulating the followup to my last post, I've also been thinking about how wonderful the Java Collections framework is. I've been singing its praises for a long time (as have others), but what's stood out to me lately is how well it encapsulates many of the most important foundational concepts of Java.
One of the reasons Java appears so intimidating and foreign to Notes developers is that the way many are introduced to it - as an auxiliary for XPages - involves learning a torrent of unrelated concepts simultaneously: strict variable typing, object equality and comparison, class casting, managed beans, recycling (though this is just as important in SSJS), confusing recycling with memory management, static methods, inheritance, interfaces, exceptions, abstract classes, database access, serialization, concurrency, and so forth. If you focus on the Collections framework specifically, though, you can learn many of the important basics in a clear way. And as a bonus, these are critical concepts shared with other, better OO languages.
Interface vs. Implementation
One of my favorite lecture topics is the value of separating interface and implementation. Unfortunately, most of the time, the values are unclear - if you're only going to make one class, is it valuable to have an interface? - and it seems like pedantry. The Collection classes, however, bring this value into sharp focus. The core interfaces correspond to elemental data structures of Computer Science and tell you everything you need to know to use them, e.g.
List: a collection of objects in the order stored by the user, accessed by index.
Map: a collection of objects accessed by a key of any type specified by the user.
Set: a collection of unique objects in either arbitrary or, for
SortedSets), automatically-sorted order.
These interfaces describe everything you need to know about the object and do so so well that there's rarely any need to specify the actual class at all outside of creation. The differences between the actual classes are, quite literally, implementation details: some focus on single-thread performance (like
ArrayList), some serve specialized cases (like
IdentityHashMap), some implement multiple useful interfaces (like
LinkedList), and some are geared towards multi-threaded use (like the
Not only does the Collection framework provide an example of a cleanly-designed interface/implementation separation, but it also provides examples of why this pays off. One of the handiest tricks is the way most of the objects (other than
Map) participate in mutual-translation schemes. By this I mean that you can take one
Collection and pass it to another (either in the constructor or via
addAll) and build a second collection of the same objects, but obeying the rules of the new collection.
For example, say you have a
List of usernames that you retrieved from a call to
view.getColumnValues(...) and now you want to sort and unique-ify them (basically like
@Sort(@Unique(...))). Well, there's a collection type for that: the
SortedSet, of which
TreeSet is the common implementation. Rather than manually building a unique, sorted
List (even using helper methods), you can accomplish this with
TreeSet's constructor. You can then add in names from other sources and maintain the same unique-and-sorted guarantee without any additional code:
SortedSet<String> names = new TreeSet<String>(view.getColumnValues(1)); names.addAll(doc.getItemValue("SomeNamesField")); names.addAll(someNameMap.values());
Because those methods only care that the incoming values are in a
Collection, the fact that some may be in
Sets, or in some other entirely-unknown type that implements
Collection is completely irrelevant. As it should be.*
This benefit is so inherent to the framework that I almost forgot to include it. Largely by the nature of the task being performed but also by virtue of proper coding, the collection objects are so reusable that they feel like part of the language, even though they're just objects like any you write. They're built to be generic and used in ways far outside of the original ken of their creators. Both the implementations and interfaces can be spread far and wide, such as with my database model collection class that also acts as a
Not all of these benefits are going to be useful in all of your code, and it will be very infrequent that you have a reason to write your own framework of this type. By learning the contours of this framework, though, you can advance your knowledge of Java and object-oriented programming generally by leaps and bounds. It will help you write cleaner code and to identify similar concepts used elsewhere.
* For example, did you know that
viewScope is not a
HashMap but actually a class called "javax.faces.component.UIViewRoot$ViewMap"? That's how much the implementation doesn't matter.