Monday, July 13, 2009

Using Groovy to See the Future of Java

Besides being a language that can be used for scripting and for writing applications, Groovy also provides the benefit of giving us a taste of new features that might be added to the Java programming language in the future. In this blog post, I will look at some of the features Groovy already supports that may one day be included in the standard Java programming language. Although these features may or may not be implemented exactly the same as they are in Groovy, Groovy's current support of these features allows us to start exploring how we might use such features to improve our development experience.

I have previously blogged about potential new Java features already demonstrable in ActionScript. Many of these same features will be discussed in this blog post, but with Groovy as the language demonstrating the features in current practice. An advantage of using Groovy for this is its closeness to the Java language. In fact, in most of my examples, I will stick very close to traditional Java syntax in order to highlight the potential new feature in Java surroundings. Groovy allows me to demonstrate potential new features of Java while using nearly completely traditional Java alongside those features. Although Groovy often does not require things like semicolons, parentheses, and return statements, I will be using these because they are required in traditional Java.


Safe Navigation Operator

I will start with one of my favorite features of Groovy that I would love to eventually see in the Java programming language. The safe navigation operator checks the object it is operating on for null before trying to access a method or member on that object. I have blogged previously on mechanisms in Java for avoiding NullPointerException and this is one I'd love to add to that list. The following code snippet demonstrates checking for null before accessing an object's method using traditional Java without the ternary operator, using traditional Java with the ternary operator, and using Groovy's safe navigation operator.


/**
* Demonstrates Groovy's safe navigation operator.
*/
public void demonstrateSafeNavigationOperator()
{
final Calendar calendar = null; // this won't be good

//
// traditional Java without ternary operator
//
System.out.print("Null Calendar: ");
if (calendar != null)
{
System.out.println(calendar.get(Calendar.YEAR));
}
else
{
System.out.println("null");
}

//
// traditional Java with ternary operator
//
System.out.println(
"Null Calendar: " + (calendar != null ? calendar.get(Calendar.YEAR) : "null"));

//
// Groovy's safe navigation operator
//
System.out.println("Null Calendar: " + calendar?.get(Calendar.YEAR));
}


I like the safe navigation operator because it's subtle, because it does not preclude any existing Java operator, and it allows the dot operator to still be used in cases when null checking is not desired. This is the operator I would most like to see added to Java.


Elvis Operator

The name alone makes this operator worth watching. The Elvis operator is a shortened version of the ternary operator which essentially says to use the initially provided value if it is non-null and not false or else use a default provided value. The following code snippet shows it in action.


/**
* Demonstrates Groovy's Elvis operator.
*/
public void demonstrateElvisOperator()
{
final String originalText = null;

//
// traditional Java without ternarary operator
//
String newTextString;
if (originalText != null)
{
newTextString = originalText;
}
else
{
newTextString = "Not Provided";
}
System.out.println("Text: " + newTextString);

//
// traditional Java with ternary operator
//
final String newTernaryTextString =
originalText != null ? originalText : "Not Provided";
System.out.println("Text: " + newTernaryTextString);

//
// Groovy's Elvis operator
//
final String newGroovyTextString = originalText ?: "Not Provided";
System.out.println("Text: " + newGroovyTextString);
}


The code above shows traditional Java without ternary, traditional Java with ternary, and the Groovy Elvis operator. The Elvis operator makes the ternary operator even more concise. Like the safe navigation operator, this can be handy in reducing encounters with NullPointerException or the ungainly code needed to avoid such encounters. I don't miss this one in traditional Java as much as I miss the safe navigation operator, however, because the Java ternary operator is available.


Switch on Strings

I blogged specifically on ActionScript allowing switching on Strings. Groovy supports this as well. The following code snippet demonstrates Groovy code switching on Strings after first showing how this is accomplished in traditional Java (with if-else if statements).


/**
* Demonstrate Groovy's ability to switch on Strings.
*/
public void demonstrateSwitchOnString()
{
final String selectedMXBean = THREAD_MXBEAN_NAME;

//
// traditional Java approach (cannot switch on String)
//

// using static imports so that ManagementFactory scoping not necessary
System.out.print("Selected MXBean: ");
if (CLASS_LOADING_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Class Loading");
}
else if (COMPILATION_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Compilation");
}
else if (GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Garbage Collection");
}
else if (MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Memory Manager");
}
else if (MEMORY_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Memory");
}
else if (MEMORY_POOL_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Memory Pool");
}
else if (OPERATING_SYSTEM_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Operating System");
}
else if (RUNTIME_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Runtime");
}
else if (THREAD_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Threading");
}
else
{
System.out.println(
"Not expecting the type of MXBean you provided: " + selectedMXBean);
}

//
// Groovy approach (go ahead and switch on the strings)
//

// leveraging Groovy's support of static imports here as well
System.out.print("Selected MXBean: ");
switch (selectedMXBean)
{
case CLASS_LOADING_MXBEAN_NAME :
System.out.println("Class Loading");
break;
case COMPILATION_MXBEAN_NAME :
System.out.println("Compilation");
break;
case GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE :
System.out.println("Garbage Collection");
break;
case MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE :
System.out.println("Memory Manager");
break;
case MEMORY_MXBEAN_NAME :
System.out.println("Memory");
break;
case MEMORY_POOL_MXBEAN_DOMAIN_TYPE :
System.out.println("Memory Pool");
break;
case OPERATING_SYSTEM_MXBEAN_NAME :
System.out.println("Operating System");
break;
case RUNTIME_MXBEAN_NAME :
System.out.println("Runtime");
break;
case THREAD_MXBEAN_NAME :
System.out.println("Threading");
break;
default :
System.out.println(
"Not expecting the type of MXBean you provided: " + selectedMXBean);
}
}


With the introduction of enums in J2SE 5, switching on Strings has been less important to me. However, I still run into situations when it would be nice. For those who don't like the switch, Groovy will, of course, support the traditional if-else if structure shown above. In fact, all of the examples in this blog post are being run in Groovy, including the "traditional Java" examples. The above example took advantage of static imports and illustrates the convenience of fabulous J2SE 5 new features support in Groovy.


Multiline Strings

Over the many years that I have developed Java code, one of the little but frequent complaints I hear is about Java's handling of multiline Strings. Groovy supports multiline Strings with its heredoc feature. One can use either three single quotes on the beginning and end of a multiline String or can use three double quotes at the beginning and end to do the same thing with the main difference being whether one wants support for GString expansion (be careful when Googling that term!).

The following code demonstrates the multiline String support in Groovy.


/**
* Demonstrate Groovy's support for multiline Strings (Heredoc).
*/
public void demonstrateMultiLineStrings()
{
//
// Groovy support for multiline Strings (Heredocs).
//
final multiLineGroovyString =
"""This is a very long String that seems to have no end and, in fact,
needs to live over more than one line in my favorite editor.""";
System.out.println("Groovy Heredoc String: " + multiLineGroovyString);
}



GString Expansion

In many of my favorite languages and frameworks, there is an ability to represent a value more significant than the text itself within a String and have that value expanded to that more significant thing when the String is expanded. Groovy supports this concept with GString expansion that uses a syntax very similar to that used for similar purposes in Ant, the Spring Framework, JSP expression language, OpenLaszlo, and Flex.

The code snippet that follows shows this expansion in action and compares it to Java's standard approach of adding such significant values via the + operator. For completeness, I have also included the ability to use varargs and java.io.Console to more nimbly include these more sophisticated values within a single String.


/**
* Demonstrate Groovy's support GStrings.
*/
public void demonstrateGStrings()
{
final Calendar today = Calendar.getInstance();

//
// traditional Java approach available since the beginning
//
System.out.println(
"Today's date is " + today.get(Calendar.DATE) + " "
+ today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US) + " "
+ today.get(Calendar.YEAR));

//
// traditional Java approach available since 1.5 introduction of varargs
// (and using Java SE 6's Console in this case).
//
System.console().printf(
"Today's date is %d %s %s%n",
today.get(Calendar.DATE),
today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US),
today.get(Calendar.YEAR));

//
// The groovy approach
//
System.out.println(
"Today's date is ${today.get(Calendar.DATE)} ${today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US)} ${today.get(Calendar.YEAR)}");
}


The advantage to the Groovy approach is that values to be evaluated can be embedded directly within the String with no need for + operators. It's a small but convenient feature.


BigDecimal Arithmetic

It is common for developers to shy away from the BigDecimal or at least groan about its use because of lack of operators such as +, -, *, and / for BigDecimal (methods with corresponding names must be called on the objects instead).

The code below demonstrates why a developer might choose to use BigDecimal. Note that this is the one code listing in this post that is truly traditional Java rather than Groovy. I include it here to demonstrate the benefit of using BigDecimal in Java.


import java.math.BigDecimal;

/**
* Simple demonstration of how BigDecimal improves precision of arithmetic
* operations.
*/
public class JavaBigDecimalExample
{
/**
* Demonstrate addition imprecision with Java doubles.
*/
public static void demonstrateDoubleAddition()
{
System.out.println("DOUBLE ADDITION");
final double augend = 99.99;
final double addend = 1.99;
final double summary = augend + addend;
System.out.println("SUM: " + summary);
}

/**
* Demonstrate better addition precision with Java BigDecimal.
*/
public static void demonstrateBigDecimalAddition()
{
System.out.println("BIGDECIMAL ADDITION");
final BigDecimal bigDecimalAugend = new BigDecimal("99.99");
final BigDecimal bigDecimalAddend = new BigDecimal("1.99");
final BigDecimal bigDecimalSum = bigDecimalAugend.add(bigDecimalAddend);
System.out.println("SUM: " + bigDecimalSum);
}

/**
* Main function for comparing arithmetic with doubles to arithmetic with
* BigDecimal.
*
* @param arguments Command-line arguments: none expected.
*/
public static void main(final String[] arguments)
{
demonstrateDoubleAddition();
demonstrateBigDecimalAddition();
}
}


When the above simple application is executed, the output looks like that shown in the next screen snapshot.


This example demonstrates the precision problems of floating point arithmetic that are avoided in traditional Java by use of BigDecimal. The next code example compares similar code to that just shown to Groovy's support for operators such as + with the BigDecimal. This certainly makes it a little easier to use and read the operations being performed on the instances of BigDecimal. It also illustrates Groovy's general support for operator overloading.


/**
* Demonstrate BigDecimal arithmetic and show that even doubles arithmetic
* in Groovy is really done with BigDecimal and that Groovy operator
* overloading is supported in Groovy.
*/
public void demonstrateBigDecimalArithmetic()
{
//
// traditional Java approach using double
// (note that Groovy treats double as BigDecimal anyway)
//
final double augend = 99.99;
final double addend = 1.99;
final double summary = augend + addend;
System.out.println("SUM (doubles): " + summary);

//
// traditional Java approach using BigDecimal
//
final BigDecimal bigDecimalAugend = new BigDecimal("99.99");
final BigDecimal bigDecimalAddend = new BigDecimal("1.99");
final BigDecimal bigDecimalSum = bigDecimalAugend.add(bigDecimalAddend);
System.out.println("SUM (BigDecmial.add): " + bigDecimalSum);

//
// Groovier BigDecimal addition (operator overloading!)
//
final BigDecimal bigDecimalSum2 = bigDecimalAugend + bigDecimalAddend;
System.out.println("SUM (BigDecimal+): " + bigDecimalSum2);
}


The output for this code is shown next. Note that the + operator worked just as well as the add method in Groovy.




Properties

A significant portion of the Java development community really wants first-class property support in Java. It's not such a big deal to me, but it is worth discussing here because Groovy provides another good example in this case. The following class is a "pure Groovy" class with all the required code listed.

example.Person.groovy

package example;

class Person
{
/**
* Groovy doesn't require the private modifier for a class's attribute to
* be treated as private.
*/
String name;

/**
* Groovy doesn't require the public modifier for a class's method to be
* treated as public.
*/
String toString() {return "Person's name is " + name;}
}


There are no "get" or "set" methods for the "name" attribute of the "Person" class, but we don't need these where we're going. The next code is some code that sets and gets the "name" of instances of Person.


/**
* Demonstrate Groovy properties.
*/
public void demonstrateProperties()
{
Person person = new Person();
person.name = "Dustin";
System.out.println(Person.getDeclaredField("name"));
System.out.println(person.name);
System.out.println(Person.getDeclaredMethod("toString"));
System.out.println(person.toString());

Person person2 = new Person();
person.setName("Samuel");
System.out.println(person.getName());
}


This code works even though get/set methods were never defined in the Person class. This is because Groovy automatically generates these methods. That is why calling them above works. Groovy property support goes even beyond automatic generation of get/set methods and allows direct access on the properties with the dot (.) operator rather than using get/set. The output shown next demonstrates Groovy's property support in action and proves that the attibute "name" was indeed private, even though it appears directly accessible with the dot operator.




Complete JavaFuturePeek.groovy Script

I have shown snippets of Groovy code throughout this post. For completeness, they are all shown together here in the script JavaFuturePeek.groovy. Note that the example/Person.groovy Groovy class and the JavaBigDecimalExample.java Java class are distinct classes not included in this overall script.

JavaFuturePeek.groovy

import example.Person;
import static java.lang.management.ManagementFactory.*;

/**
* Java-ish class written to demonstrate how Groovy features could provide a
* peek into the future of Java.
*/
public class JavaFuturePeek
{
/**
* Demonstrates Groovy's safe navigation operator.
*/
public void demonstrateSafeNavigationOperator()
{
final Calendar calendar = null; // this won't be good

//
// traditional Java without ternary operator
//
System.out.print("Null Calendar: ");
if (calendar != null)
{
System.out.println(calendar.get(Calendar.YEAR));
}
else
{
System.out.println("null");
}

//
// traditional Java with ternary operator
//
System.out.println(
"Null Calendar: " + (calendar != null ? calendar.get(Calendar.YEAR) : "null"));

//
// Groovy's safe navigation operator
//
System.out.println("Null Calendar: " + calendar?.get(Calendar.YEAR));
}

/**
* Demonstrates Groovy's Elvis operator.
*/
public void demonstrateElvisOperator()
{
final String originalText = null;

//
// traditional Java without ternarary operator
//
String newTextString;
if (originalText != null)
{
newTextString = originalText;
}
else
{
newTextString = "Not Provided";
}
System.out.println("Text: " + newTextString);

//
// traditional Java with ternary operator
//
final String newTernaryTextString =
originalText != null ? originalText : "Not Provided";
System.out.println("Text: " + newTernaryTextString);

//
// Groovy's Elvis operator
//
final String newGroovyTextString = originalText ?: "Not Provided";
System.out.println("Text: " + newGroovyTextString);
}

/**
* Demonstrate Groovy's ability to switch on Strings.
*/
public void demonstrateSwitchOnString()
{
final String selectedMXBean = THREAD_MXBEAN_NAME;

//
// traditional Java approach (cannot switch on String)
//

// using static imports so that ManagementFactory scoping not necessary
System.out.print("Selected MXBean: ");
if (CLASS_LOADING_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Class Loading");
}
else if (COMPILATION_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Compilation");
}
else if (GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Garbage Collection");
}
else if (MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Memory Manager");
}
else if (MEMORY_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Memory");
}
else if (MEMORY_POOL_MXBEAN_DOMAIN_TYPE.equals(selectedMXBean))
{
System.out.println("Memory Pool");
}
else if (OPERATING_SYSTEM_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Operating System");
}
else if (RUNTIME_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Runtime");
}
else if (THREAD_MXBEAN_NAME.equals(selectedMXBean))
{
System.out.println("Threading");
}
else
{
System.out.println(
"Not expecting the type of MXBean you provided: " + selectedMXBean);
}

//
// Groovy approach (go ahead and switch on the strings)
//

// leveraging Groovy's support of static imports here as well
System.out.print("Selected MXBean: ");
switch (selectedMXBean)
{
case CLASS_LOADING_MXBEAN_NAME :
System.out.println("Class Loading");
break;
case COMPILATION_MXBEAN_NAME :
System.out.println("Compilation");
break;
case GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE :
System.out.println("Garbage Collection");
break;
case MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE :
System.out.println("Memory Manager");
break;
case MEMORY_MXBEAN_NAME :
System.out.println("Memory");
break;
case MEMORY_POOL_MXBEAN_DOMAIN_TYPE :
System.out.println("Memory Pool");
break;
case OPERATING_SYSTEM_MXBEAN_NAME :
System.out.println("Operating System");
break;
case RUNTIME_MXBEAN_NAME :
System.out.println("Runtime");
break;
case THREAD_MXBEAN_NAME :
System.out.println("Threading");
break;
default :
System.out.println(
"Not expecting the type of MXBean you provided: " + selectedMXBean);
}
}

/**
* Demonstrate Groovy's support for multiline Strings (Heredoc).
*/
public void demonstrateMultiLineStrings()
{
//
// Groovy support for multiline Strings (Heredocs).
//
final multiLineGroovyString =
"""This is a very long String that seems to have no end and, in fact,
needs to live over more than one line in my favorite editor.""";
System.out.println("Groovy Heredoc String: " + multiLineGroovyString);
}

/**
* Demonstrate Groovy's support GStrings.
*/
public void demonstrateGStrings()
{
final Calendar today = Calendar.getInstance();

//
// traditional Java approach available since the beginning
//
System.out.println(
"Today's date is " + today.get(Calendar.DATE) + " "
+ today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US) + " "
+ today.get(Calendar.YEAR));

//
// traditional Java approach available since 1.5 introduction of varargs
// (and using Java SE 6's Console in this case).
//
System.console().printf(
"Today's date is %d %s %s%n",
today.get(Calendar.DATE),
today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US),
today.get(Calendar.YEAR));

//
// The groovy approach
//
System.out.println(
"Today's date is ${today.get(Calendar.DATE)} ${today.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US)} ${today.get(Calendar.YEAR)}");
}

/**
* Demonstrate BigDecimal arithmetic and show that even doubles arithmetic
* in Groovy is really done with BigDecimal and that Groovy operator
* overloading is supported in Groovy.
*/
public void demonstrateBigDecimalArithmetic()
{
//
// traditional Java approach using double
// (note that Groovy treats double as BigDecimal anyway)
//
final double augend = 99.99;
final double addend = 1.99;
final double summary = augend + addend;
System.out.println("SUM (doubles): " + summary);

//
// traditional Java approach using BigDecimal
//
final BigDecimal bigDecimalAugend = new BigDecimal("99.99");
final BigDecimal bigDecimalAddend = new BigDecimal("1.99");
final BigDecimal bigDecimalSum = bigDecimalAugend.add(bigDecimalAddend);
System.out.println("SUM (BigDecmial.add): " + bigDecimalSum);

//
// Groovier BigDecimal addition (operator overloading!)
//
final BigDecimal bigDecimalSum2 = bigDecimalAugend + bigDecimalAddend;
System.out.println("SUM (BigDecimal+): " + bigDecimalSum2);
}

/**
* Demonstrate Groovy properties.
*/
public void demonstrateProperties()
{
Person person = new Person();
person.name = "Dustin";
System.out.println(Person.getDeclaredField("name"));
System.out.println(person.name);
System.out.println(Person.getDeclaredMethod("toString"));
System.out.println(person.toString());

Person person2 = new Person();
person.setName("Samuel");
System.out.println(person.getName());
}

/**
* Main function that is run when the command "groovy JavaFuturePeek" is
* executed at the command-line.
*/
public static void main(final String[] arguments)
{
final JavaFuturePeek me = new JavaFuturePeek();
me.demonstrateSafeNavigationOperator();
printSeparatorLine();
me.demonstrateElvisOperator();
printSeparatorLine();
me.demonstrateSwitchOnString();
printSeparatorLine();
me.demonstrateMultiLineStrings();
printSeparatorLine();
me.demonstrateGStrings();
printSeparatorLine();
me.demonstrateBigDecimalArithmetic();
printSeparatorLine();
me.demonstrateProperties();
}

/**
* Print blank line to standard output.
*/
public static void printSeparatorLine()
{
System.out.println(System.getProperty("line.separator"));
}
}


Much more could have been done to make this script "groovier," but I intentionally wanted it to be close to traditional Java to allow the focus to be on what new features in Java might be like.


Script Output

The overall output of the script is not all that exciting, but is shown next.




Conclusion

Groovy provides an excellent playground for trying out features that may be coming to future versions of the Java programming language. Its close relationship to Java makes it even more attractive for this than other less Java-like languages with the same features. In this blog posting, I have attempted to demonstrate Groovy features that may end up in a future version of Java. These proposed new Java features include property support, BigDecimal operators, switch on String, multiline Strings, and the Elvis operator. Other than the brief mention of BigDecimal operator overloading, I did not focus on Groovy's operator overloading here for the same reason I did not mention Groovy's closure support here: they both deserve blog posts of their own. I instead focused on smaller new features here that might someday be added to traditional Java.

2 comments:

Sumit said...

You don't need your System.out before the println

@DustinMarx said...

Sumit,

Thanks for leaving a comment. I intentionally included the System.out even though Groovy does not require it to leave the general code as traditional Java-like as possible so that emphasis would be on the potential future features of Java already available in Groovy.