Wednesday, April 10, 2013

Readable checks for null/blank/empty...

Sometimes the initialization of or assignment to String and List variables in Apex can be confusing. Here's a quick quiz for you. Which assertions below will fail (assuming that a failed assertion doesn't stop execution)?
public class AddressController {
  //--------------------------------------------------------------------------
  // Properties
  public String street { get; set; }
  public String zipCode;

  //--------------------------------------------------------------------------
  // Constructor
  public AddressController() {
    System.assertEquals(null, street);
    System.assertEquals('', street);

    System.assertEquals(null, zipCode);
    System.assertEquals('', zipCode);

    Account[] accounts = [SELECT Id FROM Account WHERE Name = 'bogus'];
    System.assertEquals(null, accounts);
    System.assertEquals(new Account[] {}, accounts);
  }
}
Now what if street is bound to an input control on a Visualforce page and submitted without anything being entered. Will it be null or ''?

During my first few months as an Apex developer, I created countless bugs related to this issue. I couldn't seem to keep all the rules straight. Eventually, I had statements like the one below all over the place. I finally decided to end the madness and stop typing statements like this:
  if ((null != street) && (0 != street.trim().length()) &&
    (null != accounts) && !accounts.isEmpty()) {
    ...
  }
And instead, write this:
  if (!SF.nullOrEmpty(street) && !SF.nullOrEmpty(accounts)) {
    ...
  }
Here's my little utility function that saves me time and makes my code more readable - nullOrEmpty():
/**
 * @author Steve Cox
 * @description utility method for determining if an object is null or blank.
 * For strings, 'blank' means empty or only whitespace. A 'blank' List has
 * no elements.
 *
 * WARNING this method doesn't work with sets or maps
 *
 * @param o the object to test 
 * @return true if the specified object is null, empty, or only whitespace
 */
public static Boolean nullOrEmpty(Object o) {
  return (null == o) ||
    ((o instanceof String) && (0 == ((String)o).trim().length())) ||
    ((o instanceof List<object>) && (0 == ((List<object>)o).size()));
}

P.S. I'd love to learn how to make this work with Sets and Maps. Somehow, Set<String> is not recognized as an instanceof Set<object> and Map<String,String> is not recognized as an instanceof Map<Object,Object>. Please comment below if you've got ideas.

2 comments:

  1. Great utility Steve! BTW, I believe you can handle the string comparisons by using String.isBlank() which checks against null, blank ('') and whitespace (' ').

    ReplyDelete
    Replies
    1. I did some testing with String.isBlank, and it doesn't appear to handle whitespace - at least in the version I'm working with. Still... a handy function. Let me know if you see something different. Thank you for the kind words, Anthony!

      Delete