Making Java Groovy at JavaOne 2013

Monday morning I gave my “Making Java Groovy” presentation at JavaOne in San Francisco. This is my first trip to JavaOne, and the sheer size of it is rather overwhelming. Of course, it’s also obvious at almost every turn that JavaOne is the weak sister of Oracle Open World, but hey, it could have been IBM, right?

I’ve been thinking about this presentation for literally years. I give a related talk on the No Fluff, Just Stuff tour all the time, and I’ve done similar talks at DevNexus, Gr8conf.us, and more. But this is JavaOne — the heart of the Java universe — and doing well here is supposed to mean something. Besides, if the evals are good enough, you can become a JavaOne Rock Star, which is likely the closest I’ll ever get to being any kind of rock star, so there’s that.

The biggest thing I worried about, though, was condensing my talk into an hour. On the NFJS tour I’m accustomed to 90 minute blocks, and frankly I can talk about Groovy all day long (and frequently do in training classes). I kept cutting parts I liked, though, until I thought I had something reasonably condensed.

Perhaps it’s not surprising, therefore, that I was much more nervous for this talk than normal. Nate Schutta (fellow NFJS speaker and one of the best presenters I’ve ever met) likes to remind me that we speakers are an odd breed. It’s pretty strange to find somebody who both likes to code and likes to stand up in front of an audience and talk about it. Actually, it’s weird enough to do the latter. Most people find public speaking terrifying. Somehow we manage to like it and even look forward to it.

Since this was my only talk at the conference (probably a mistake on my part; I should have submitted more proposals), I knew I could use everything I had. At NFJS conferences, I worry that after two or three talks, everybody’s heard all my jokes, which is a serious disadvantage. Of course, that assumes they listened to them in the first place. 🙂

I still had time after setting up, so I did a couple of things to entertain the audience before the mics were turned on. One was that I went to http://speedtest.net and checked the speed of the ethernet connection, which came in at around 85 Mbps. Sweet. I did this while the projector was on, partly just to see the reaction and partly to make the “wow, I could have downloaded that whole Breaking Bad episode in five minutes” joke.

That is a joke, of course. I would never do anything our corporate masters at the MPAA (and the RIAA) wouldn’t want, and if I did, I certainly wouldn’t admit it in a public forum like this.

Then I opened up a tab in my browser to http://emergencykitten.com . I liked refreshing the page anyway, but I claimed I did it in case anything in the presentation went wrong, which might even have been true.

I noticed, btw, that there is a GitHub link on emergencykitten.com, which says that the site apparently runs using nodejs on Heroku. If you look in the data folder there, you’ll find kittens.yml (!), which contains the links to the Flikr pages for all the images.

I’ll come back to that in a moment.

JavaOne wanted me to use PowerPoint, which was a singularly uncomfortable proposition. I used it anyway, but felt free to make fun of it during the talk, especially because it became balky every time I switched from it to a demo and back. The slides are mostly for summaries anyway, since my talk is almost completely code based, but I did add some fun meme-based pictures.

For example, check out this one:

grinds_my_gears

The slides themselves are now on slideshare.net, so you can see the whole presentation here.

I start with a demonstration that you can execute compiled Groovy code with the Java command, if you just add the “groovy-all” jar file to your classpath. That’s a cool demo, but it also shows that you don’t need to modify your production Java environment at all in order to add Groovy to your system.

Then I talk about operator overloading, and how even though I don’t do it a lot, it shows up in all sorts of places in the Groovy JDK.

I jumped from there to POGOs, and wrote a POGO that had a couple of attributes and AST transformations for @ToString, @EqualsAndHashCode, and @TupleConstructor, before replacing all three with @Canonical.

I then did my favorite demo of accessing and using the Groovy v3 geocoder. The code for that is in my book’s GitHub repository in a couple of different places, along with Spock tests for both online and offline (!) usage.

This nice image followed:

success_kid_mjg

I went from there to my default JSON example, which is still based on ICNDB: the Internet Chuck Norris Database. When I use it now I generally use the firstName and lastName properties so I can replace “Chuck Norris” with Guillaume Laforge, or Paul King, or even Brian Goetz. As luck would have it, the site was up and running, so I was able to do that, too.

That leads to my favorite image:

most_interesting_mjg

I said I would come back to the Flikr issue, and since I want to add something technical to this post, I’ll do that here. Flikr has a REST API, but it’s rather more involved than I like to use in a presentation. Here’s my basic search:

String key = '...my key...'
String endPoint = 'http://api.flickr.com/services/rest?'
def params = [method: 'flickr.photos.search', api_key: key,
    format: 'json', tags: 'cat', nojsoncallback: 1,
    media: 'photos']

I’m searching for cat pictures, which as everyone knows is the reason the Internet was invented. Most of the parameters are required, but I used nojsoncallback=1 to keep from wrapping the JSON response in a method. The only tag I submit is ‘cat‘, but I might want to add some more because that doesn’t always give me what I want. You have to be careful with Flikr, though, because you can get back almost anything.

I assemble the needed URL and submit it in the usual way:

def qs = params.collect { it }.join('&')
String jsonTxt = "$endPoint$qs".toURL().text
def json = new JsonSlurper().parseText(jsonTxt)

Turn the map entries into list elements of the form “key=value” then join with an &; convert the string to a URL and download the text, then parse it with a JsonSlurper. This stuff becomes automatic after a while.

The complication with Flikr is that the response doesn’t actually include the image URL. Instead it breaks it into pieces, which I have to reassemble:

def urls = []
json.photos.photo.each { p ->
    urls << "http://farm${p.farm}.staticflickr.com/${p.server}/${p.id}_${p.secret}.jpg"
}

Drilling down into each photo element, I can extract the farm, server, id, and secret elements and use them to create the URL for the actual image. This I then insert into a Java ImageIcon:

new SwingBuilder().edt {
    frame(title:'Cat pictures', visible: true, pack: true,
        defaultCloseOperation: WC.EXIT_ON_CLOSE, 
        layout:new GridLayout(0, 2)) {
        urls[0..5].each { String url ->
            label(icon:new ImageIcon(url.toURL()))
        }
    }
}

The only odd part of this is the WC constant, which is an alias for javax.swing.WindowConstants. I build a GUI with six cat pictures in it, and I’m done.

Putting it all together (so you can copy and paste if you want — change the key to your own to make it run):

import groovy.json.*
import groovy.swing.SwingBuilder

import java.awt.BorderLayout as BL
import java.awt.GridLayout
import javax.swing.ImageIcon
import javax.swing.WindowConstants as WC

String key = '...insert your key here...'
String endPoint = 'http://api.flickr.com/services/rest?'
def params = [method: 'flickr.photos.search', api_key: key,
    format: 'json', tags: 'cat', nojsoncallback: 1,
    media: 'photos']
    
def qs = params.collect { it }.join('&')
String jsonTxt = "$endPoint$qs".toURL().text
def json = new JsonSlurper().parseText(jsonTxt)

def urls = []
json.photos.photo.each { p ->
    urls << "http://farm${p.farm}.staticflickr.com/${p.server}/${p.id}_${p.secret}.jpg"
}

new SwingBuilder().edt {
    frame(title:'Cat pictures', visible: true, pack: true,
        defaultCloseOperation: WC.EXIT_ON_CLOSE, 
        layout:new GridLayout(0, 2)) {
        urls[0..5].each { String url ->
            label(icon:new ImageIcon(url.toURL()))
        }
    }
}

I ran the script and got two cat pictures and four pictures of tractors. I was confused until somebody pointed out that they were probably Caterpillar tractors, which shows what kind of trouble you can get into on Flikr. Believe me, it could have been worse.

Bringing it back to the GitHub repo for emergencykitten.com, maybe in the future I’ll grab the Flikr URLs in their repo and do the same thing. Still, this is a reasonably cool demo, and why not live a bit dangerously anyway?

The talk went well and a good time was had by all. At least I hope so, though I won’t know for sure until I see the session surveys and find out of I’m a rock star after all.

For the record, I’d like to say that as Groovy Rock Stars go, if Paul King is Led Zeppelin, Guillaume Laforge is the Rolling Stones, and Graeme Rocher is the Beatles, then I want to be E.L.O. I always liked E.L.O. and owned several of their albums when I was a kid.

Actually, that’s not quite right. I didn’t own several of their albums. I owned several of their 8-track tapes. Whoa.

I have to say, though, that the most interesting experience of all was seeing my book on the shelves of the bookstore at the conference. I went in late in the day and was about to frown at the number of copies on the shelf when somebody walked up, took one, browsed through it, grinned wryly to himself, and actually bought the bloody thing. I was extremely tempted to introduce myself and offer to sign it or take a picture of him holding it, but in the end I decided that would be too creepy.

My wife, who came along to do sightseeing in San Francisco, agreed with that. Still, I’ll be at the conference until Thursday, so if anyone wants me to add my chicken scratch to their book, just ask around. I promise not to hover too close to the store.

Making Java Groovy: JavaRanch this week

This week Making Java Groovy is the featured book at JavaRanch, where I’ve been a member for many years. JavaRanch is yet another of Kathy Sierra’s (and Bert Bates’s) contributions to the community, which started out as a certification study site and evolved into an excellent message board. Until StackOverflow came along, it was my favorite place for getting answers to technical questions. It’s still an excellent example of how a friendly non-trolling technical community can operate.

If you drop by and ask a question in the Groovy forum, you can win a copy of my book.

Last week was a very busy one for me. I gave four talks at the NFJS event in Seattle, then went from there to the SpringOne2GX conference in Santa Clara, and then did the NFJS event in Boston area (Framingham, to be specific). I had a great time at all three events.

One of my favorite moments was in my “Advanced Groovy: Tips and Tricks” talks, when I noticed Paul King walk into the room. If you’ve read any of my book, you know I’m a big fan of his. In addition to being a Groovy committer, he’s also a co-author on Groovy in Action. If you’re interested in anything Groovy, be sure to check out his presentations at SlideShare.net.

While I was able to handle most of the questions, Paul did show me a few items after I was finished. For example, running collect on a map is even easier than I thought. To build a query string, I used to do this:

String qs = [a:1, b:2, c:3].collect { k,v -> "$k=$v" }.join('&')
assert qs == 'a=1&b=2&c=3'

Paul noticed I did that in a blog post and reminded me that the toString method on Map.Entry returns “key=value”, so I can reduce that to:

String qs = [a:1, b:2, c:3].collect { it }.join('&')
assert qs == 'a=1&b=2&c=3'

It turns out that now there’s even a default collect method that returns it, so I can make this even simpler:

String qs = [a:1, b:2, c:3].collect().join('&')
assert qs == 'a=1&b=2&c=3'

I need the parentheses on the collect method (or the compiler will think it’s a property), but it’s hard to get any simpler than that.

Another interesting question came up when I was talking about metaprogramming. For years I knew you could add a method to a class by adding a new property to its metaclass. For example, I can add a cook method to Map:

Map.metaClass.cook = { BigDecimal purity -> "$purity% pure meth" }
assert '85% pure meth' == [a:1, b:2].cook(85)

(Sorry about the drug example. Like so many people, I’m totally caught up in the last few episodes of Breaking Bad. And btw, I have no idea what the actual ingredients are for meth, though I know one of them involves ephedra because I have to sign for my allergy medication at the pharmacy now.)

The question that came up was, can I overload that method using the same technique? I can certainly create a closure with different arguments, but can I assign it to the same property without messing up the original assignment?

Here’s an alternative implementation of the cook method:

import java.awt.Color

Map.metaClass.cook = { Color c -> "$c-tinted meth" }
assert 'java.awt.Color[r=0,g=0,b=255]-tinted meth' == 
    [a:1, b:2].cook(Color.blue)

I can assign the two different implementations to the same property:

import java.awt.Color

Map.metaClass.cook = { Color c -> "$c-tinted meth" }
Map.metaClass.cook = { BigDecimal purity -> "$purity% pure meth" }
assert '85% pure meth' == [a:1, b:2].cook(85)
assert 'java.awt.Color[r=0,g=0,b=255]-tinted meth' == 
    [a:1, b:2].cook(Color.blue)

It still looks funny to me, but it works under the hood. Good to know.

Paul would probably appreciate it if I also mention that the example I chose was my own. He didn’t suggest drugs at any stage of development.

If you get a chance, please drop by the Groovy forum at JavaRanch this week and say hi. As another reminder, my Making Java Groovy presentation at JavaOne is Monday morning at 10am, so if you’re there, please drop by. I gave away over 20 print copies of my book at SpringOne2GX, and I’m sure I’ll have more at JavaOne. I’ll even deface sign one for you if you like. 🙂

Trains on the Range

I’m at SpringOne2GX this week, but this morning before my first presentation I noticed the following in my Twitter feed:

DailyGrailsTip_ranges

First of all, I think that’s the first time anything of mine has ever appeared in @DailyGrailsTip, so yay for me. On the other hand, the link is to a script that isn’t terribly self explanatory. At the risk of spoiling part of my Advanced Groovy Tips and Tricks talk, I thought I’d discuss it here.

The basic idea is that any Groovy class can be made into a range if it:

  1. implements java.util.Comparable
  2. has a next method
  3. has a previous method

My example uses a TrainStation class, which forms a doubly-linked list when I place them along a track. So part of the implementation is trivial:

class TrainStation implements Comparable<TrainStation> {    
    // Links to next and previous stations
    TrainStation next
    TrainStation previous
    
    TrainStation next() { next }
    TrainStation previous() { previous }    

    @Override  // implement Comparable
    int compareTo(TrainStation ts) {
        // Hmm. What to do here?
    }    
}

The TrainStation has a reference to the next one and the previous one, and the implementations of next and previous just return them. (I considered making a wrapper class called Track, but kept it simple here.)

The question is, how am I going to implement Comparable? Since one important characteristic of train stations is their physical location, I decided to use latitude and longitude, and made the comparison based on latitude.

The result is:

import java.text.NumberFormat

class TrainStation implements Comparable<TrainStation> {
    
    // Links to next and previous stations
    TrainStation next
    TrainStation previous
    
    // location attributes
    String city
    String state
    
    // set by Geocoder service
    BigDecimal latitude
    BigDecimal longitude

    TrainStation next() { next }
    TrainStation previous() { previous }
        
    @Override
    int compareTo(TrainStation ts) {
        this.latitude <=> ts.latitude
    }
    
    String toString() { 
        NumberFormat nf = NumberFormat.instance
        "$city, $state \t (${nf.format(latitude)}, ${nf.format(longitude)})" 
    }
}

Each train station has a city and state, which I set, and a latitude and longitude, which are computed from a geocoder. My geocoder is based on Google’s RESTful web service, which I’ve used many times before:

class Geocoder {
    public static final String BASE = 'http://maps.google.com/maps/api/geocode/xml?'

    void fillInLatLng(TrainStation station) {
        String encoded = 
            [station.city, station.state].collect { 
                URLEncoder.encode(it,'UTF-8')
            }.join(',') 
        String qs = [sensor:false, address: encoded].collect { it }.join('&')
        def response = new XmlSlurper().parse("$BASE$qs")
        station.latitude = response.result[0].geometry.location.lat.toBigDecimal()
        station.longitude = response.result[0].geometry.location.lng.toBigDecimal()
    }
}

The fillInLatLng method takes a train station and URL encodes its city and state, so that they can be sent as part of a URL. It then builds a query string by converting the map entries in the form “key:value” into list entries in the form “key=value”.

(Note: I used to write that as [a:1, b:2].collect { k,v -> "$k=$v" }. Then Paul King pointed out that the default toString method on Map.Entry is in fact “key=value”. Therefore I can just write [a:1, b:2].collect { it } and it does the same thing. Live and learn.)

After building the query string, I use the parse method from XmlSlurper to access the web service, download the resulting XML, parse it into a DOM tree, and hand me the root. The rest is just walking the tree, keeping in mind that sometimes Google returns more than one request, so I made sure to use the first one only by writing result[0].

To use the new class, I need to create some train station and set their next and previous properties. Then I can use them in a range. Here’s a script to do all that:

// Amtrak, NE corridor (mostly)
TrainStation was = new TrainStation(city:'Washington', state:'DC')
TrainStation bal = new TrainStation(city:'Baltimore', state:'MD')
TrainStation wil = new TrainStation(city:'Wilmington', state:'DE')
TrainStation phl = new TrainStation(city:'Philadelphia', state:'PA')
TrainStation nwk = new TrainStation(city:'Newark', state:'NJ')
TrainStation nyc = new TrainStation(city:'New York', state:'NY')
TrainStation nhv = new TrainStation(city:'New Haven', state:'CT')
TrainStation pvd = new TrainStation(city:'Providence', state:'RI')
TrainStation bos = new TrainStation(city:'Boston', state:'MA')

// Arrange the stations on the track
was.next = bal; bal.previous = was
bal.next = wil; wil.previous = bal
wil.next = phl; phl.previous = wil
phl.next = nwk; nwk.previous = phl
nwk.next = nyc; nyc.previous = nwk
nyc.next = nhv; nhv.previous = nyc
nhv.next = pvd; pvd.previous = nhv
pvd.next = bos; bos.previous = pvd

def ne_corridor = [was, bal, wil, phl, nwk, nyc, nhv, pvd, bos]

// Fill in all the latitudes and longitudes
Geocoder geo = new Geocoder()
ne_corridor.each { station -> 
    geo.fillInLatLng(station)
}

// range heading north
println '\nNorthbound from WAS to BOS'
(was..bos).each { println it }

// range heading south
println '\nSouthbound from BOS to WAS'
(bos..was).each { println it }

// subrange from nyc to bos
println '\nNYC to BOS'
(nyc..bos).each { println it.city }

The resulting output is:

Northbound from WAS to BOS
Washington, DC (38.907, -77.036)
Baltimore, MD (39.29, -76.612)
Wilmington, DE (39.746, -75.547)
Philadelphia, PA (39.952, -75.164)
Newark, NJ (40.736, -74.172)
New York, NY (40.714, -74.006)
New Haven, CT (41.308, -72.928)
Providence, RI (41.824, -71.413)
Boston, MA (42.358, -71.06)

Southbound from BOS to WAS
Boston, MA (42.358, -71.06)
Providence, RI (41.824, -71.413)
New Haven, CT (41.308, -72.928)
New York, NY (40.714, -74.006)
Newark, NJ (40.736, -74.172)
Philadelphia, PA (39.952, -75.164)
Wilmington, DE (39.746, -75.547)
Baltimore, MD (39.29, -76.612)
Washington, DC (38.907, -77.036)

NYC to BOS
New York
New Haven
Providence
Boston

There you have it. I’ve taken an arbitrary class, TrainStation, and enabled it to be used in a Groovy range. This whole demo is part of the code I use in my “Advanced Groovy” talk, stored in my GitHub repository at https://github.com/kousen/AdvancedGroovy .

Quick book update: The ebook version of Making Java Groovy should be released today. The print book should be available from Manning on 9/16 or 9/17. The remaining mobile formats (epub and mobi) will be released on 9/27. I’m still hoping to have some physical copies here at the conference to give away, but I haven’t seen them yet.

Btw, it’s wonderful seeing so many of the Groovy and Grails team members at the conference. I seriously doubt, however, that any of them took the train to get here. 🙂

Making Java Groovy: Operator Overloading

This is partly a spoiler for my “Making Java Groovy” talk that I normally do at NFJS events and plan to use in my similar talk at JavaOne, but to celebrate the book going to the printer I thought I’d try to contribute something technical here.

I’ve always felt that one of the keys to understanding Groovy was operator overloading. Operator overloading has a mixed reputation, conjuring up horror stories of developers changing + to be *, which might be good for job security but not good in general.

(Ultimately it’s not be good for job security, either.)

To be honest, I don’t do a lot of operator overloading in my own code, but I take advantage of it all the time. Most Groovy developers don’t realize how thoroughly operator overloading occurs throughout the Groovy JDK, among other places.

This web page lists all the operators in Groovy and their corresponding methods. What’s easy to overlook, however, is that every operator in Groovy actually invokes a method. That table doesn’t just provide you with options — it’s what’s actually happening under the hood. Note that it’s not just operators like + or * that call methods. Array-like access (the [] operator) calls getAt and putAt on maps, and even == invokes the equals method.

(Actually, == invokes the compareTo method if the class happens to implement java.util.Comparable, but according to the Comparable contract that ought to be equivalent.)

The code simplifications that result are dramatic. For example, consider the plus method in java.util.Date, which takes an int representing the number of days:

Date now = new Date()
Date then = now + 3
assert 4 == (now..then).size()

It’s also why you can not only concatenate with strings using +, you can also subtract substrings from them using the minus operator:

String s = 'silly string'
assert 'more silly string' == 'more ' + s
assert 'string' == s - 'silly '

Note that in each case, the operator is returning a new string rather than modifying the original. Strings are still immutable, even in Groovy.

Let me give a larger example that uses Groovy / Java integration to make coding easier. If you’ve ever done financial calculations in Java (or if you’ve ever seen Office Space), you know you need to use java.math.BigDecimal for the amounts rather than just doubles. But BigDecimal in Java is an annoying class to work with, because (1) you have to instantiate it, (2) you have to remember to use the constructor that takes a String, or the results are not predictable (see the JavaDocs for details), and (3) you lose all your operators and have to use all the associated method calls, keeping in mind that (4) all instances of the class are immutable. That makes for a rather verbose class when all you want is to wrap a numerical value.

You can do all that, or you can take advantage of the fact that the native data type for floating point values in Groovy is already BigDecimal. Not only that, but the Groovy JDK includes methods like plus, minus, and multiply in the java.lang.Number class (the superclass of BigDecimal), so you can use your normal operators. The result is a class like:

package com.mjg

import groovy.transform.Canonical

@Canonical
class Account {
    BigDecimal balance = 0.0
    
    BigDecimal deposit(BigDecimal amount) {
        balance += amount
    }
    
    BigDecimal withdraw(BigDecimal amount) {
        balance -= amount
    }
}

The only attribute is the balance, stored as a BigDecimal, initialized to zero. Since the attribute lacks a public or private modifier, Groovy automatically generates public getter and setter methods for it. The deposit and withdraw methods take advantage of operator overloading to update the balance using simple + and – operators. Finally, the Canonical AST transformation adds equals, hashCode, and toString methods to Account, as well as a “tuple” constructor that can be called from Java. That’s a lot of mileage for 16 lines of code.

Before I use it from Java, let me verify that it works so far:

package com.mjg

// Normal Groovy ctor that sets the property
Account acct = new Account(balance:250)
assert 250 == acct.balance
assert 300 == acct.deposit(50)
assert 200 == acct.withdraw(100)

// check the tuple ctor
acct = new Account(150)
assert 150 == acct.balance

// toString method
assert 'com.mjg.Account(150)' == acct.toString()

// equals and hashCode
Set accounts = [new Account(balance:250), new Account(250)]
assert accounts.size() == 1

Accounts can be created using the normal map-based constructor or the tuple constructor, which in this case has only a single argument (because the class has a single attribute). Accessing the balance property actually invokes the getBalance method, which is supplied by Groovy automatically. The generated toString method is as shown, and to prove that both equals and hashCode are working I added two identical accounts (instantiated by either constructor) into a Set and verified that Groovy added only one of them.

That’s all well and good, but if I can’t verify a Groovy class using Groovy tests, I might as well give it up. Instead, I want to access the Groovy class from Java. Here, therefore, is a highly contrived Bank class, in Java:

package com.mjg;

import java.math.BigDecimal;

public class Bank {
    public Account openAccount(double initialBalance) {
        Account acct = new Account();
        acct.deposit(new BigDecimal(initialBalance + ""));
        return acct;
    }

    public BigDecimal getBalance(Account acct) {
        return acct.getBalance();
    }
    
    public BigDecimal addFunds(Account acct, double amount) {
        return acct.deposit(new BigDecimal(amount + ""));
    }
    
    public BigDecimal removeFunds(Account acct, double amount) {
        return acct.withdraw(new BigDecimal(amount + ""));
    }
}

The Bank has a method to open an account using a double, which is wrapped in a BigDecimal using the String-based constructor. Then addFunds and removeFunds just delegate to the associated methods in Account.

To test this, I might as well switch back to Groovy again, but in a normal JUnit test:

package com.mjg

import static org.junit.Assert.*;

import org.junit.Test;

class BankTests {
    Bank bank = new Bank()
    Account acct = bank.openAccount(250);
    
    @Test
    void testOpenAccount() {
        assert acct
    }
    
    @Test
    void testGetBalance() {
        assert 250 == bank.getBalance(acct)
    }
    
    @Test
    void testAddFunds() {
        bank.addFunds(acct, 50)
        assert 300 == bank.getBalance(acct)
    }
    
    @Test
    void testRemoveFunds() {
        bank.removeFunds(acct, 100)
        assert 150 == bank.getBalance(acct)
    }
}

All the tests work without a problem, demonstrating how easy it is to switch from Groovy to Java and back again. If you ever have to add code to an existing Java system that involves BigDecimal, writing a class in Groovy is a very easy way to do it.

In my book, incidentally, the chapter on Spring and Groovy uses a similar Account class, but with an added id. Then I implement an AccountDAO interface (written in Java, of course) using the a Groovy class that takes advantage of Spring’s JdbcTemplate (again from Java), complete with Spock tests. If you want to see the source code now, just check out ch07 in the GitHub repository for the book.

As in this example, when I code in Groovy I don’t often do operator overloading, but the effects of it show up everywhere. Hopefully these simple examples will help you take advantage of it in the future.

BTW, I received notice from Manning that I will have actual hard copies of Making Java Groovy at the SpringOne2GX conference next week. Even if you don’t want one, please come by and say hello. Don’t be surprised if I can’t stop grinning the entire time.

%d bloggers like this: