Tuesday, September 14, 2010

How I Learned to Stop Worrying and Love Java's Ternary Operator

I have long felt that one of the attributes of the best software developers is an ability to learn new things and to even change their opinions as appropriate as they learn new things or are exposed to differing ideas that refine and enhance their thinking.  Often, the best developers already have enough experience to have mostly solid opinions that in many cases are only strengthened as they gain additional experience.  However, the best developers can admit when their long-held beliefs may no longer apply or are sufficiently challenged to deserve a second look.

In this, my 500th blog post, I look at how and why I changed from loathing the Java ternary operator to embracing it.  Just five years ago, I absolutely despised the ternary operator and wondered why it was even in the Java language.  Today, I'm one of its biggest fans.  This post's primary motivation is not to win any converts over to the "dark side" of the ternary operator, but is instead to demonstrate how my one-time firmly and deeply held opinion gradually changed to almost the opposite extreme.  I like to tell myself that my ability to change opinions on this so dramatically is evidence that I'm not become too conceited to be able to learn and be open to new ideas.

During my first several years of Java development experience, I could not understand why anyone would ever choose to use it.  It seemed too cryptic and reminiscent of those crazy coding challenges where people competed to see who could squeeze the most functionality into a single line of C or Perl or machine language code. I've always valued code readability and maintainability (and still do) and at the time felt that the ternary operator was nearly unreadable and difficult to maintain.  That opinion held without even the slightest hint of weakness for several years until one event finally challenged my thinking on the subject.

Several years into my Java development career, I was tasked with the unenviable job of creating many "data" classes that would be used with Hibernate.  Because of the nature of this class, it was necessary to add equals(Object) and hashCode() methods to these numerous objects.  I originally created those methods with my favorite IDE of the day, but didn't quite like how the code looked in the equals(Object) implementation.  I also wanted to verify that the IDE-generated equals(Object) methods met all the advertised contract details for that method and that these implementations met the recommendations from Josh Bloch in Effective Java.

I didn't realize it immediately, but this was the beginning of my moving from a ternary hater to a ternary fan. In Chapter 3 ("Methods Common to All Objects") of Effective Java, Bloch provides insight into a suitable equals() implementation in Item 7 ("Obey the general contract when overriding equals"). When Bloch outlines a "recipe for a high-quality equals method," his step #4 in this recipe is to "check to see if [every 'significant'] field of the argument matches the corresponding field of this object."  He then demonstrates how this can be done to avoid a NullPointerException and uses the ternary operator to demonstrate this.  When I read this, I realized that the ternary operator made the numerous conditionals in the equals methods much more concise.  I decided then to try out the same idiom in my implementations.

After implementing several equals methods with the ternary operator, I gained familiarity with it and soon started to find it to be less unreadable than I had previously thought.  This led me to using it in situations beyond implementations of equals methods and that increased use led to even greater comfort with it.  I soon realized that not only did I find it often as readable as its more verbose counterpart, but I actually started to feel it was more readable!

Another factor in my conversion to ternary fan was my increasing use of scripting languages.  I particularly enjoyed using Ruby around the same time and Groovy would later fill a similar niche for me.  Using these languages contributed to my feeling more comfortable with the concise syntax of the ternary operator.  Groovy goes even further than the traditional ternary operator with its Elvis operator.

As I gained familiarity and a level of comfort with the Java ternary operator, I started to see other situations in which I actually preferred it over the more verbose alternatives.  I have always liked to use the Java keyword final when declaring locally scoped variables whose references should not change within that scope.  Unfortunately, without the ternary or use of a temporary variable, there was no good way to use final on a locally declared variable specified based on the results of a condition.  The following simple code snippet demonstrates this difference.

Assigning Variable Based on Condition Without Ternary Operator
/**
    * Accept the reference type Double and return the corresponding primitive
    * double (a manual unboxing).
    *
    * @param value Double to be converted/unboxed to double.
    * @return Primitive double corresponding to provided referencfe type Double
    *    that returns Double.NaN (not a number) if null is provided.
    */
   public double demonstrateVerboseConditionalVariableSetting(final Double value)
   {
      double returnValue = Double.NaN;
      if (value != null)
      {
         returnValue = value.doubleValue();
      }
      return returnValue;
   }


Assigning Variable Based on Condition with Ternary Operator
/**
    * Accept the reference type Double and return the corresponding primitive
    * double (a manual unboxing).
    *
    * @param value Double to be converted/unboxed to double.
    * @return Primitive double corresponding to provided referencfe type Double
    *    that returns Double.NaN (not a number) if null is provided.
    */
   public double demonstrateTernaryVariableSetting(final Double value)
   {
      final double returnValue =  value != null
                                ? value.doubleValue()
                                : Double.NaN;
      return returnValue;
   }

The interesting thing is that in the particular example above, use of the ternary operator makes it possible to make this method even more concise. No local variable even need be explicitly defined as shown in the next code listing.

No Explicit Variable Defined
/**
    * Accept the reference type Double and return the corresponding primitive
    * double (a manual unboxing).
    *
    * @param value Double to be converted/unboxed to double.
    * @return Primitive double corresponding to provided referencfe type Double
    *    that returns Double.NaN (not a number) if null is provided.
    */
   public double demonstrateTernaryNoExplicitVariable(final Double value)
   {
      return value != null ? value.doubleValue() : Double.NaN;
   }

All three of the above code listings work essentially the same, but the ternary operator makes the latter two versions (and especially the last version) very concise. I realized after using the ternary operator for a couple of weeks that I found these forms more readable than the longer, more verbose form.

Besides the direct use of the ternary operator for more concise (and now to me more readable) code, I also found its use helped me to find other places in the code that I could use other tactics to make more concise.  However, the major unanticipated benefit I discovered when becoming fond of the ternary operator is how familiarity with it made it easier to understand the SDK documentation. For example, the Javadoc documentation for the List interface states "Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2))" in explaining the List's equals(Object) implementation.  It uses the ternary operator to specify how equality between elements of a List is evaluated.  The Javadoc for Map.Entry presents two ternary operators to explain what makes two Map.Entry entries equal:

(e1.getKey()==null ?
 e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
(e1.getValue()==null ?
 e2.getValue()==null : e1.getValue().equals(e2.getValue()))

Having familiarity and even a preference for the ternary operator makes it easier to read and understand the documentation.  Clearly it is easier to specify the rules in the documentation with the ternary operators than without them.

I've also found it useful to benefit from my own experience disliking the ternary operator in the past.  Although I'm a big advocate of the ternary operator now, it is easy for me to understand that not everyone is because I wasn't too long ago myself.  Also, I still don't like using too many ternary operators together in the same statement or nested ternary operators unless the alternative is too complicated itself.  Having come from hating the ternary operator, I know better than to think that everyone loves it.  As readable I think the ternary operator can make my code, I also appreciate that the ternary operator may not always be best if the other people who work with my code cannot read it or find it painful to read and maintain.

Experienced developers are often good at quickly understanding promising technologies and separating these from over-hyped technologies.  However, an experienced developer and even a new developer can fall into the trap of not being willing to learn new things or not being willing to challenge old assumptions and biases.  For me, my dramatic change from ternary operator hater to ternary operator fan is evidence that I'm still able to learn new things and overcome previous biases when there is sufficient reason to do so.

7 comments:

O.Weiler said...

Actually it's called the Conditional Operator. Sure, there is only one ternary operator in Java at the moment (and most probably always will be) but if Oracle would introduce another ternary operator one day :-)...

Unknown said...

I love this puzzler:

Object o = true ? new Integer(1) : new Double(0.0);
System.out.println(o);

Rémi

@DustinMarx said...

O. Weiler,

Thanks for the feedback. I suppose that's technically correct about the name ternary operator being better stated as the conditional expression operator in case Java ever gets another ternary operator. That being said, I agree with you that it will proably always be just the single instance of a ternary operator in Java and I think it's fairly safe to talk about it with that name and most Java developers will know what one is talking about. I've often heard it (mistakenly) called the tertiary operator as well.

This reminds me of the background of JDBC. Despite Sun's insistence to the contrary, many people have claimed that JDBC stands (is an abbreviation) for Java Database Connectivity. At this point, it might as well be.

Thanks for pointing this out. It definitely adds to the discussion and background on the conditional operator.

@DustinMarx said...

Rémi,

Thanks for adding that puzzler. It has caused its share of trouble and it's certainly not obvious (to me at least) that the expression will print out "1.0" and that the Object is a java.lang.Double.

I'm really glad you pointed this out because I will say that I'm not completely enamored with the ternary conditional operator in certain puzzling situations or where it makes code less readable for most developers. Order of precedence issues can also present nasty surprises with casual use of this operator.

I like your puzzler so much that I may write a short blog post on it in the future.

@DustinMarx said...

Andrew Glover's post Concisely operating with ternaries demonstrated that Glover had a similar experience as I did: his use of dynamic languages led him from choosing not to use the ternary operator to preferring the ternary operator.

He states: "In the past, I used to eschew the ternary due to its terseness– I found myself using conditionals to more clearly express my intent; however, after years of dynamic language conditioning, I’ve found myself reserving my stance on the ternary."

@DustinMarx said...

The post Java Auto-Unboxing Gotcha. Beware! talks about an issue with auto unboxing and ternary operator.

@DustinMarx said...

A discussion regarding the ternary operator in Java occurred recently in the java subreddit.