Remove The Fluff With Google Guava

John Hoestje Development Technologies, Java 2 Comments

Attention: The following article was published over 11 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

As a part of the “lazy” programmer club, I try to find and use things that have solved problems similar to mine. In Java, we all have small problems that need to be solved everyday. How many times has an Object.equals() method been implemented in single application? How many unit tests does it take to verify the Object.equals() logic works? The same thing applies to file I/O, reflection, Strings, and so much more. As trivial as some of these things seem, it is easy enough to miss a small detail. Time and lines of code can be saved by using something that already exists. In the end, productivity goes up and bugs go down.

I don’t want to spend a lot of effort trying to read code when it just isn’t necessary. I want to see method names that describe what they are doing. To my eyes, it is easier to understand what a method does when it is named as checkNotNull(T) than when the name only contains notNull(T). The intent of notNull(T) could be misinterpreted to mean to convert T to a not null value. It is little things that make code more descriptive, easier to read, and a better use of my time. I also don’t want to read a  lot of lines of code consisting of int i = 0;. All that fluff just confusicates the code and takes the focus away from the parts that really matter. It’s better to be resourceful.

Google Guava

Guava has become one of my favorite libraries. Guava was born from Google’s internal labs. The libraries contained in Guava were found to be the most useful utilities during development of their projects. Unlike some other utility frameworks, Guava takes advantage of Java features like generics and reflection to simplify and keep the use of the API clean. According to the Guava Wiki, Google releases new fixes and functions every three months. If you don’t keep up with the bleeding edge updates, be aware that deprecated methods are removed after 18 months. Although the frequent API changes may cause some code changes, it does help keep the framework clean.

A major goal of Guava is to make code more descriptive and a whole lot cleaner. Take Object.equals() for example. Instead of implementing equals() filled with a bunch of if statements, Guava enables you to fill equals() with a bunch of Objects.equal() methods. Which one sounds easier to create and which one sounds easier to read? The functionality that matters still exists and that is if the proper comparisons are being made. The syntactic fluff surrounding the important parts are removed. It is easier to verify if the correct properties are being compared when all you see is the comparisons being made. Where was Guava 10 years ago when I needed it?

Guava also provides utilities for more advanced operations. An immutable collections library is provided to help keep your data thread-safe, known, and consistent. Guava also tries its hand at some functional features. Since Java is not a functional programming language, it is easy to get into a heap of trouble when trying to fit Java into that paradigm. The Guava Wiki gives a warning to the user on how Functions and Predicates may be easily misused. As you peruse the list of utilities on Guava’s Wiki, you’ll see that the following examples are just a taste of what Guava offers to the “lazy” (read: resourceful) programmer. I only included some of the frequently used items in the Basic Utilities section. Hopefully this helps show that Guava isn’t just another utilities library that will boost your productivity.

Object Method Assistance

The Objects class helps the developer accurately and easily implement the equals(), hashCode(), toString(), and compareTo() methods. As you’ll witness, the descriptive nature of Guava is much easier to read instead of a bunch of if statements and variables. Even though we have confidence in our code, isn’t it nice to know the code used in Guava has been tested many more times than we could ever hope for?

Take a basic Employee entity:

public class Employee implements Comparable<Employee> {
    private long id;
    private String title;
    private String firstName;
    private String lastName;

    //getters and setters
}

Even for simple entity classes, it is a good idea to override the Object methods to properly represent the object instance.

@Override
public boolean equals(final Object obj) {
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }
    if (this == obj) {
        return true;
    }
    Employee otherEmployee = (Employee) obj;
    //I don’t want to read this...
    return (this.getId() != otherEmployee.getId()
    && (this.getLastName() == null) ? otherEmployee.getLastName() == null
    : this.getLastName().equals(otherEmployee.getLastName())
    && (this.getFirstName() == null) ? otherEmployee.getFirstName() == null
    : this.getFirstName().equals(otherEmployee.getFirstName())
    && (this.getTitle() == null) ? otherEmployee.getTitle() == null
    : this.getTitle().equals(otherEmployee.getTitle()));
}

That isn’t very much fun to write, read, or debug. The developer reading the main property comparison logic has to slow way down to understand what is going on. It isn’t very descriptive and is easily enough to get something wrong. Typos are harder to discover with all the syntax involved in the comparisons.

Let’s see how Guava can help us:

@Override
public boolean equals(final Object obj) {
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }
    if (this == obj) {
        return true;
    }
    Employee otherEmployee = (Employee) obj;
    return Objects.equal(this.getId(), otherEmployee.getId())
    && Objects.equal(this.getLastName(), otherEmployee.getLastName())
    && Objects.equal(this.getFirstName(), otherEmployee.getFirstName())
  && Objects.equal(this.getTitle(), otherEmployee.getTitle());
}

Isn’t that much more enjoyable to write and read? We “lazy” programmers like it. With Objects.equal(), it is very easy to comprehend the intent. It doesn’t take much time or effort to read. The ease of use should also reduce the chance of introducing a typo. Only the important pieces are left. It would be very easy to catch if a comparison was incorrectly written as Objects.equal(this.getLastName(), otherEmployee.getFirstName()).

Now on to the Object.toString() method implemented the traditional way:

@Override
public String toString() {
    return String.format(
   "Employee{Id=%d, lastName=%s, firstName=%s, title=%s}",
    getId(), getLastName(), getFirstName(), getTitle());
    //creates => Employee{id=1, lastNight=Smith, firstName=John, title=President}
}

Not too bad, but I don’t like working with String literals. No tool is going to let me know if I messed up the format. Object.toString() is a very handy helper when it comes to debugging and viewing the logs for information. So any help in writing these methods is just a boost to productivity.

An assist by Guava’s Objects.toStringHelper():

@Override
public String toString() {
    return Objects.toStringHelper(this).add("id", getId())
    .add("lastName", getLastName())
    .add("firstName", getFirstName()).add("title", getTitle())
    .omitNullValues().toString();
    // Employee{id=1, lastNight=Smith, firstName=John, title=President}
    // or if title was null....
    // Employee{id=1, lastNight=Smith, firstName=John}
}

A nice benefit to ToStringHelper is that I don’t have to worry about the property types. It all works whether I am adding a String, int, or any other type. If only the value is needed without a label, addValue() can be used. Another benefit of using the utilities provided by Guava is the addition of helper methods. ToStringHelper gives us the option of omitting the values that are null with the omitNullValues() method. Of course, you didn’t need me to give you a redundant sentence describing what the method provides.

Now it’s hashCode() time:

@Override
public int hashCode() {
    int hash = 1;
    int prime = 31;
    hash = hash * prime + (int) getId();
    hash = hash * prime
    + (getLastName() == null ? 0 : getLastName().hashCode());
    hash = hash * prime
    + (getFirstName() == null ? 0 : getFirstName().hashCode());
    hash = hash * prime
    + (getTitle() == null ? 0 : getTitle().hashCode());
    return hash;
}

Again, not too bad. If computing the hash code is always the same for every implementation, why keep writing it?

@Override
public int hashCode() {
    return Objects.hashCode(getId(), getLastName(), getFirstName(), getTitle());
}

Again, wasn’t that much more enjoyable to read and write? Guava allows the developer to focus on the parts that matter and not on the code that can be templated out. It is hard to get it wrong with Guava.

Comparing objects is similar to how Object.equals() and Object.hashCode() works. It is just as painful and verbose. So, not surprisingly, Guava can help us here too. Implementing Comparable.compareTo() is necessary to support ordering and has bigger consequences if not implemented correctly. Implementing the method correctly so that properties such as transitive is upheld adds to the complexity. So why not let Guava help us out?

The traditional compareTo():

public int compareTo(final Employee otherEmployee) {
    int compareResult = Long.compare(getId(), otherEmployee.getId());
    if (compareResult != 0) {
        return compareResult ;
    }
    compareResult = getFirstName().compareTo(otherEmployee.getFirstName());
    if (compareResult != 0) {
        return compareResult ;
    }
    compareResult = getLastName().compareTo(otherEmployee.getLastName());
    if (compareResult != 0) {
        return compareResult ;
    }
    return getTitle().compareTo(otherEmployee.getTitle());
}

With Guava’s Help:

public int compareTo(final Employee otherEmployee) {
    return ComparisonChain.start()
    .compare(this.getId(), otherEmployee.getId())
    .compare(this.getFirstName(), otherEmployee.getFirstName())
    .compare(this.getLastName(), otherEmployee.getLastName())
    .compare(this.getTitle(), otherEmployee.getTitle())
    .result();
}

Just like all the Objects utility methods, Guava makes implementing the compareTo() method a breeze. The only thing the developer needs to remember is to add all the properties needing to compare and to place the properties with the highest chance of differing first in the comparing chain. The comparison gets started with start() and ends with result(). Each compare() is called, until a non-zero result is found. This is why it is still important to place the property with the highest chance of difference first. As with the other utility methods, all the fluff is removed to provide a concise, easy to read, and painless method. It is harder to make an accidental typo that the compiler won’t catch. If you do accidently swap inputs, like .compare(otherEmployee.getId(), this.getId(), it will be pretty visible within the chain.

Guava’s additional comparison utilities provided by the Ordering can be used with ComparisonChain.compare(T, T, Comparator). Ordering.usingToString() can be passed in instead of using natural ordering for a basis of comparison. Ordering provides additional capabilities to manipulate collections and examine values. It is handy to have a isOrdered() method to check if an Iterable is already ordered. Since it is a good idea to not change lists you don’t own, sortedCopy() and immutableSortedCopy() are provided to perform as their name suggests; to return a sorted copy of the list.

Avoiding Nulls With Optional

Nulls have caused so many headaches. No one wants to see the dreaded NullPointerException or have another null check condition in an if statement. I’ve been bitten by the null bug more times that I would like to admit. So, what can we do about it?

Since a null value is ambiguous and can be often misunderstood, it is frequently recommended not to introduce or use nulls in application code. One way to indicate an absence of a value is to use Guava’s Optional. Not only does Optional give you something to return instead of null to indicate an absence of a value, it also forces the developer to think about the case where a value doesn’t exist whereas a null is easily forgettable. Guava uses this null adverse philosophy in many of its utilities. When you read the documentation, you’ll find that many methods will throw an exception when a null is encountered. Most of the time, there is very little use of trying to continue processing a null value. In other cases, knowing something doesn’t exist is of worth and may dictate a different processing course. Guava can assist with both scenarios. According to Guava’s Wiki, there are facilities to help ease the use of nulls and to help avoid them altogether. As we all know, unless we have control of all the code in the application, we always must be aware of the possibility of a value being null.

Guava provides the Optional<T> class to replace a nullable reference with a non-null value. It also provides a little different way of describing T. A value is either present or absent. As you can guess, a null reference is absent. It helps deal with null in a more descriptive manner. and bringings it to the front and center of the developer’s focus. Optional<T> makes it easier and more descriptive for the application code to alter processing course if a null is encountered.

To wrap an object that may be null and have a default value returned:

Optional<Long> possibleId = Optional.fromNullable(employee.getId());
possibleId.or(-1L);  //returns if value is not present
possibleId.isPresent(); // returns true
possibleId.get(); // returns Long
Optional<String> possibleTitle = Optional.of(employee.getTitle());  //throws exception if null

The hard part with incorporating the use of Optional<T> into a system is to decide where the boundaries of Optional <T>’s existence should be. If a query is performed to select an Employee by name, should a null be returned or an Optional<T>? Should all parameters be Optional<T> or none? Whether you are writing new code or refactoring existing code, Optional<T> applies better for returning values. It gives the advantage to the calling code to force it to think about a null scenario. The calling code has to actively unwrap the object instead of blindly assuming a value exists. I find it to be more descriptive (and reassuring) to ask if the returned object is present.

Optional<Employee> optionalEmployee = dao.findEmployeeByName(“Jones”);
if (optionalEmployee.isPresent()) {
    Employee employee = optionalEmployee.get();
    …...

Checking Parameters with Preconditions

What about parameters you ask? It is recommended to use Guava’s Preconditions like checkNotNull(T) for parameters. Preconditions are concise and descriptive whereas other common frameworks are a little too brief. As stated before, notNull(T) is ambiguous and doesn’t return the object passed in. Importing the Precondition methods statically is recommended and adds to the ease of readability. Preconditions will throw an exception if its check fails. If a parameter is in a state the application code can’t handle, then there isn’t a reason to continue processing.

It is also handy to use checkNotNull(T) in a constructor since it returns T if T is present. Being able to do this in one line helps the readability of the constructor. It isn’t fun to add an if statement for each parameter to check for null.

Single line check:

this.firstName = checkNotNull(firstName);

Each Precondition utility is overloaded to add possible debugging information with the thrown exception. This is important so the logs will contain better information for trying to figure out what bad data is being passed in.

Precondition checks:

checkNotNull(id);
checkArgument(id > -1, "Expected id > -1, but id is %s", id);
checkArgument(id > -1, someErrorMessageObject);

Simplifying Exception Propagation with Throwables

Right or wrong, dealing with exceptions is almost an afterthought for most developers. Either an overly broad catch is created to catch all possible exceptions, or one catch for just a single specific exception, or a combination of the two. A specific exception may be added to be caught because the calling method API enforces it, and a broad exception type will be added as a catch-all. Most developers think about exception handling just enough to make the compiler happy. A new exception is added to be caught only when that exception was found to be thrown. The new exception type is usually when the code is in production.

try {
    //do something
    } catch (SpecificException se) {
    // I can handle this
    } catch (Throwable t) {
    // handle everything else
}

Throwables.propagateIfInstanceOf() gives the developer an option to have a default catch with a broad exception type. There may be cases where you only want to propagate a few specific exceptions from this single catch block. Throwables.propagateIfInstanceOf() can help reduce the number of lines of code needed to accomplish this. The registered exception class is compared with the instance of the exception within propagateIfInstanceOf(), and will be thrown only if the exception instance is of that type. Throwables.propagate() can satisfy the compiler that one of the registered exceptions will be thrown.

public void foo() throws ApplicationException, FileNotFoundException  {
    try {
        //do some work;
        } catch (NullPointerException e) {
        // I can handle this;
        } catch (Throwable t) {
        Throwables.propagateIfInstanceOf(t, ApplicationException.class);
        Throwables.propagateIfInstanceOf(t, FileNotFoundException.class);
        throw Throwables.propagate(t);
    }
}

Another handy utility offered to exceptions is for helping with the exception stack. By the time an exception is bubbled up to your code, it can get pretty messy and deep. Throwables offers getRootCause(), getCasualChain(), and getStackTraceAsString(). They don’t need much explaining for what they do – their name describes what they do. The nice thing about getting a list from getCasualChain() is you can use Guava’s Iterables utilities class to filter for specific exceptions to enhance debugging and logging. There can also be some value in dealing only with the root exception with getRootCause().

Guava: A Solution To My Problems

I can keep my card with the “lazy” (read: resourceful) programmers club with Google Guava. Guava keeps the focus on what is important in code development and takes away all the fluff. Implementing Object methods shouldn’t be complicated, painful, or hard to read. I can stay “lazy” because Guava does everything for me. I only have to make sure the correct properties are present and are being compared to other correct properties. My “lazy” eyes don’t want to read a lot of syntactic fluff. Inline comments are not needed due to Guava’s concise, but descriptive method names. Productivity goes up when methods such as equals() are made up of equal() statements. Time isn’t wasted on complicated if statements. An added benefit is that the Guava code has been very well tested.

These examples are just scratching the surface. Hopefully you can see the differences of what Guava gives you over hand-jamming Object methods, or even over other utility frameworks. Give it spin and see what you find. Here’s where to start.

— John Hoestje, [email protected]

0 0 votes
Article Rating
Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments