I have been getting more interested with the capabilities of the Groovy language since I have used it more and more as part of my daily work on a Grails web application. I have also been having some interesting conversations with one of my co-workers about the possibilities provided by Groovy’s MOP (Meta Object Protocol) for creating new ways of expressing meaning in the contextual space of a given problem area using DSLs (Domain Specific Languages). I have seen a few examples of augmenting existing Java classes with new functionality by using Categories wrapped around the existing classes, but have mainly seen these related to distances and conversion of units of measure within that domain. I thought back to one of the first programs I wrote (can’t remember the language, maybe it was C) for doing temperature conversion between degrees Fahrenheit and Celsius. So, I thought I would give an example of using Groovy Categories to add new functionality to existing Java classes.

The problem

So let’s assume that you are tasked with writing a temperature conversion application. This converter should be able to convert temperatures given in either degrees Fahrenheit or Celsius and give the result in degrees Fahrenheit. Sounds easy enough, you can model things within the class(es) exactly how you need to solve the problem. But wait! There is another stipulation. You are told that there is already an existing class in some existing archaic Java library that was written ten years ago, and you have you use this class within your code. You think, alright, even if the class is wretched, I can write some new Java code and extend the existing class to offer the new functionality. Presenting the Java class you are required to use:

AncientTemperature.java

package com.asoftwareguy.temperature; /** * This is an "old" existing Java class representing temperature. * It assumes the unit of measure is degrees Fahrenheit. * */ public final class AncientTemperature { // in degrees Fahrenheit private int temp; public AncientTemperature(int temp) { this.temp = temp; } public String toString() { return new StringBuilder().append("The current temperature is " + temp + " degrees F.").toString(); } }

The class is declared final! So the creator of this class, a decade or so ago, found it in their infinite wisdom that their representation of temperature was perfect and no one would ever need to add any more functionality. Awesome! :/

The solution

You could obviously also create new functionality by creating a decorator class and using composition to delegate calls when necessary out to the existing class, but you are using Groovy, so why would you do that! Here the requirements stated above are as follows:

We need to be able to give the temperature inputs in either degress Fahrenheit or Celsius. We need to be able to give the resulting temperature in degrees Fahrenheit. We have to use the existing AncientTemperature.java class.

Requirement #3 is de facto by mandate, so let’s ignore that one. After looking at what we have to work with, it seems that we have requirement #2 already met by the functionality of the existing class. The toString() method spits out the temperature in degrees Fahrenheit:

package com.asoftwareguy.temperature; public final class AncientTemperature { // other methods public String toString() { return new StringBuilder().append("The current temperature is " + temp + " degrees F.").toString(); } }

Let’s look at how we can meet requirement #1 using Categories. Categories in Groovy are somewhat similar to the concept of static extension methods in C# with one key difference: In C#, you can only add new methods to the class which you are extending; you cannot override methods that already exist in the class. In Groovy, you can add new methods as well as override existing methods in the class you are extending! This a powerful feature and one that I will use in this example. Let’s take a look at our Category:

TemperatureConversion.groovy

package com.asoftwareguy.temperature class TemperatureConversion { static AncientTemperature getFahrenheit(String fahrenheit) { new AncientTemperature(fahrenheit as int) } static AncientTemperature getCelsius(String celsius) { BigDecimal fahrenheit = (celsius.toInteger() * (9/5) + 32) fahrenheit = fahrenheit.setScale(0, BigDecimal.ROUND_DOWN) new AncientTemperature(fahrenheit.toString() as int) } static AncientTemperature getFahrenheit(Integer fareheit) { new AncientTemperature(fareheit) } static AncientTemperature getCelsius(Integer celsius) { BigDecimal fahrenheit = (celsius * (9/5) + 32) fahrenheit = fahrenheit.setScale(0, BigDecimal.ROUND_DOWN) new AncientTemperature(fahrenheit.toString() as int) } }

If you have done any kind of temperature conversion before, the code above should look familiar. It uses the standard formulas for converting temperature between degrees Fahrenheit and Celsius, and vice-verse. With this code in place, it allows you to write code like the following, using a ‘use’ block in Groovy:

TemperatureTest.groovy

package com.asoftwareguy.temperature use(TemperatureConversion) { assert "100".fahrenheit.toString() == 'The current temperature is 100 degrees F.' println "100".fahrenheit assert "50".fahrenheit.toString() == 'The current temperature is 50 degrees F.' println "50".fahrenheit assert "32".fahrenheit.toString() == 'The current temperature is 32 degrees F.' println "32".fahrenheit assert "100".celsius.toString() == 'The current temperature is 212 degrees F.' println "100".celsius assert 100.fahrenheit.toString() == 'The current temperature is 100 degrees F.' println 100.fahrenheit assert 50.fahrenheit.toString() == 'The current temperature is 50 degrees F.' println 50.fahrenheit assert 32.fahrenheit.toString() == 'The current temperature is 32 degrees F.' println 32.fahrenheit assert 100.celsius.toString() == 'The current temperature is 212 degrees F.' println 100.celsius }

All of the methods defined in TemperatureConversion.groovy only add new functionality to the AncientTemperature.java class. The ‘use’ block is key to this working, as it provides that any types declared within the block expose the methods of the Category. I had mentioned earlier that in Groovy, you can also override existing methods and I also said I would give an example. So here is the version of the Category with overriding methods:

TemperatureConversion.groovy

package com.asoftwareguy.temperature class TemperatureConversion { // other methods static AncientTemperature plus(AncientTemperature first, AncientTemperature second) { int tempFirst = first.temp int tempSecond = second.temp; int newTemp = tempFirst + tempSecond return new AncientTemperature(newTemp) } static AncientTemperature minus(AncientTemperature first, AncientTemperature second) { int tempFirst = first.temp int tempSecond = second.temp; int newTemp = tempFirst - tempSecond return new AncientTemperature(newTemp) } }

As you can see above, we have actually overrode the ability to add and subtract objects of AncientTemperature with each other. We can do this even though we do not have this ability in Java because by default, Groovy adds the plus and minus (along with many other methods) to all objects. So now we can write code like this:

package com.asoftwareguy.temperature use(TemperatureConversion) { // other methods assert (100.fahrenheit + 50.fahrenheit).toString() == 'The current temperature is 150 degrees F.' println 100.fahrenheit + 50.fahrenheit assert (100.fahrenheit + 0.celsius).toString() == 'The current temperature is 132 degrees F.' println 100.fahrenheit + 0.celsius assert (100.fahrenheit - 0.celsius).toString() == 'The current temperature is 68 degrees F.' println 100.fahrenheit - 0.celsius }

Groovy Categories are a powerful tool in providing new functionality to existing types at run-time and for creating DSLs that can describe your problem space. Hopefully this has provided you with a good example of using Groovy Categories to add such functionality.

All of the source code for this post can be found in this Gist.