Maybe you know the deal: everytime, a new project gets started, among the first things you write are functions and classes which you have already written a dozen times before. A filtering mechanism for collections, yet another iterable wrapper for enumerations, yet another implementation of "Pair".
After Marcel and me had been working together for a few years, and found ourselves doing exactly this stuff over and over again (or even worse: copy old classes or code), we decided, that it is time to change our ways. Thus we threw together our respective function collections, weeded out duplicates, brought it all to a common style, and thus colllib was born.
Basically, colllib functions do not throw checked exceptions at all. (If there are exceptions to this rule, there is a specific logical reason.) This may seem unusual, but follows the same principles as the collection framework itself (e.g. ArrayIndexOutOfBounds exception is also a runtime exception.)
This is also true (and here it gets critical) for functions using reflection to read or evaluate properties from a class. This is a deliberate tradeoff between usability and safety. Thus, we prefer:
// Risk of runtime exception, if anyone renamed the field "name" in the respective bean CollectionUtil.applyFilter(myInput, FilterCollection.beanValueEquals("name", "SF.net"))to
try { // checked exceptions fantasy CollectionUtil.applyFilter(myInput, FilterCollection.beanValueEquals("name", "SF.net")) } catch(MeaninglessExcption e) { doSomeExceptionHandling(e); } catch(AnotherMeaninglessExcption e) { doSomeExceptionHandling(e); } catch(YetAnotherMeaninglessExcption e) { doSomeExceptionHandling(e); } catch(StillAnotherMeaninglessExcption e) { doSomeExceptionHandling(e); } catch(IThinkReflectionCodeThrowsFiveMeaninglessTypesOfExcption e) { doSomeExceptionHandling(e); }
The "apply to collection" part normally uses the main colllib utility class
CollectionUtil
. Depending on the nature of the small code snippets
this centers around Filters, Operators, Transformers, or Generators.
These interfaces basically have the following signature:
Interface | Signature | Explanation |
---|---|---|
Filter | E → boolean | Map any element to a bool value. Also known as "predicate" in other approaches. |
Operator | (E, E) → E | Combine to elements to a single result element. |
Transformer | E → F | Convert one value of type E to another value of type F following a given rule. |
Generator | E → (F, F, ...) | Create a collection of values from a single value |
:-)
... we try, please bear with us...