The Devil is in the details
I want code to be simple-n-short, on-point and easy to read. Unnecessary complexity distract and obscure understanding of what is really going on and can be a real killer for productivity.
You know, tangled for-loops and indexes to track, if/else and switch cases, null/validation checks, converting/copying/deleting/sorting collections, exception handling … the list goes on along with ever-increasing line numbers and maintenance burden.
An excellent quote by Tony Hoare comes to mind.
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.
In other words: The Devil is in the details.
Apache Commons have some of most wonderful libraries complementing the JDK APIs, but this post is not about Commons. It is about Google Guava which is similar to Commons in many regards. It provide a single library for commonly used day-to-day tasks, such as collection handling, string manipulation, concurrency, IO, primitives, exceptions etc.
There is so much nice stuff in Guava and I wont have time to go through the complete library, but here at least some examples of what it can do.
Objects makes it easy to implement hashcode/equals without cluttering your classes too much (Eclipse auto-generation tends to be a bit verbose for my taste).
Classes that implement toString are really pleasant to use when doing debugging and logging, but can be a real pain implement. Objects.toStringHelper makes this really easy and also help maintaining a consistent format for printing objects.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30||
Printing this class outputs something like this.
Wrapping the original exception object is not always appropriate, because it can cause ClassNotFoundException in the client code if communication occur between unrelated class loaders or if they are serialized on the wire. Throwables can decouple this dependency, still allowing remote clients to see the stack trace by converting it to a string.
|1 2 3 4 5||
Concatenating two separate collections and performing operations on the result can cause a quite a lot of clutter. Iterables to the rescue. Take a minute and think how code might look without Iterables.concat.
|1 2 3||
Multimap is like a Map, but allow multiple values to be stored for every key. The following example is a a variant of a typesafe hetereogeneous container using multimap to realize a product catalogue of items.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19||
BiMap implement a one-to-one bidirectional relationship between key and value of the Map. Here is an example using language code to get the language and vice versa.
|1 2 3 4 5 6||
Most classes have restrictions on values given them in constructor and methods. Invalid values should be escalated as soon as possible by doing explicit validity checks before execution. It is a lot better to fail-fast than to fail later with an unexpected exception or worse, silently compute the wrong result.
|1 2 3 4 5||
Constraints are similar to preconditions in a way that they can restrict what values are added to a collection. This makes collections much easier to use and code a lot cleaner, since constraints are separated from business code.
|1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17||
Predicates and Functions
Predicates evaluate if something is true or false but can also be combined into more complex evaluations using “and”, “or”, “not” and “in”.
What normally would require a for-loop and bunch of if statements can now be reduced to a one-liner. How sweet is that?
|1 2 3 4 5 6 7 8 9 10||
You can also use Maps.filterKeys or Iterables.filter in a similar way. But keep in mind that removal from modification is bidirectional. e.g. removal from the origin affect result and vice versa.
Functions on the other hand, is a way of transforming one object to another. For example, convert concurrency on a order of items.
|1 2 3 4 5 6 7 8||
You can also use Maps.transformValues or Iterables.transform in a similar way.
A Query API
I have been think about how to create simple but powerful Fake Objects for some time now. But I dont want fakes themselves to turn into a maintenance burden, so they must be easy to implement. My intuition tells me i need a general purpose state management framework for this to work. And so using predicates, I created a small fluent query interface interacting with an in memory storage.
|1 2 3 4 5||
I feel quite satisfied with the result actually – short, compact, understandable and typesafe.
I hope these examples will trigger you to explore Guava further and use it to make your code more readable, robust and maintainable.
And lastly, I really do hope many of these facilities reach standard Java some day soon.