(dc..bos): Train Stations as a Groovy Range

Posted by: Kenneth Kousen on 06/16/2012

I’ve been working on a presentation about interesting features in Groovy, and I came up with an example that I like but is probably too long to do in the available time, so I thought I’d show it here. The idea is to illustrate how any class can be made into a Groovy range by implementing the right methods.

Actually, my larger theme is that understanding Groovy (and the Groovy JDK in particular) is helped considerably by thinking about operator overloading. I don’t do a lot of operator overloading in my own code (present example excepted), but it appears all over the place in the Groovy library. I find it helps Java developers understand Groovy better if they know that whenever they see any operator, they should mentally convert it to a method. For example, + is the plus method, – is minus, and, better yet, [...] is getAt or putAt and even as is really asType.

This helps Java devs understand that by reading the groovydocs for String, they realize they can do things like:

String s = 'this is a string'
assert 'this' == s[0..3]
assert 'ing' == s[-3..-1]
assert 'gni' == s[-1..-3]
assert 'thisisastring' == s[0..3, 5..6, 8, -6..-1]
assert 'th is a string' == s - 'is'

and so on.

A groovy range is simply two values separated by a pair of dots, as shown in the previous example. Many classes can be used in a range, as in::

assert [1, 2, 3, 4, 5] == 1..5
assert ['a', 'b', 'c'] == 'a'..'c'
Date now = new Date()
Date then = now + 2
assert ['Jun 16', 'Jun 17', 'Jun 18'] == (now..then)*.format('MMM dd')

(Adjust the last one based on when you run the script, of course. And how cool is it that the Groovy JDK adds a format method to the Date class?)

As the most excellent book Groovy in Action points out (2nd edition available through the Manning Early Access Program), any class can be used in a range if it:

  • implements the java.util.Comparable interface
  • has a next method
  • has a previous method

That’s all you need. I wanted to show an example of this, and based on all my traveling I decided to use train stations. Here’s my first pass at it:

class Station implements Comparable<Station> {
    String name
    Station next
    Station previous
    int position
    
    Station next() { next }
    Station previous() { previous }
    
    int compareTo(Station s) {
        this.position - s.position
    }
    String toString() { name }
}

My Station class is really a node in a doubly-linked list. It has a name and pointers to the next station and the previous station. To make the class implement Comparable, I decided to assign each station an integer position as I added it to a track.

My next step was to use put stations together. At first I was going to use an addStation method, and then I realized that’s really what the plus method was all about. So instead I did this:

    Station plus(Station s) {
       s.position = ++position
       s.previous = this
       this.next = s
       return s 
    }

Here’s a script using the Station class:

Station dc = new Station(name:'DC')
Station phl = new Station(name:'PHL')
Station nyc = new Station(name:'NYC')
Station bos = new Station(name:'BOS')

// operator overloading to make a route:
dc + phl + nyc + bos

// Stations are a range in each direction:
def northBound = (dc..bos)*.name
def southBound = (bos..dc)*.name

assert northBound == ['DC', 'PHL', 'NYC', 'BOS']
assert southBound == ['BOS', 'NYC', 'PHL', 'DC']

That’s all there is to it. I could easily add a minus method to Station in order to remove a node, and if I ever have to use this class in a real system I probably will. The position value is only used for comparison, so the actual number doesn’t matter, but I can imagine that if I have to add and remove lots of stations I’ll need some way to make that more rigorous. I also can’t escape the feeling that a better design would involve a Track class to hold the resulting route, but I didn’t need it for this simple demonstration.

Finally, those of us who occasionally travel the Acela route up and down the northeast corridor know that I left out a lot of stations, but I suppose I can dream that someday our trains will be in the same class (no pun intended) as their European or Asian counterparts. :)

(Obligatory marketing: My book Making Java Groovy, also available through the Manning Early Access Program, just went out for its 2/3rds review. I hope to be “text complete” by the end of the summer, for a dead treeware release just in time for the holiday gift-giving season.)



About Kenneth Kousen

Kenneth Kousen

Ken Kousen is the President of Kousen IT, Inc., through which he does technical training, mentoring, and consulting in all areas of Java and XML. He is the author of the O'Reilly screencast "Up and Running Groovy", and the upcoming Manning book about Java/Groovy integration, entitled "Making Java Groovy".

He has been a tech reviewer for several books on software development. Over the past decade he's taught thousands of developers in business and industry. He is also an adjunct professor at the Rensselaer Polytechnic Institute site in Hartford, CT. His academic background includes two BS degrees from M.I.T., an MS and a Ph.D. from Princeton, and an MS in Computer Science from R.P.I.

More About Kenneth »

Northern Virginia Software Symposium

November 1 - 3, 2013

Reston, VA

Current Topics on the NFJS Tour

  • Core Java, JEE
  • Dynamic Languages: Groovy, JRuby, Scala, Clojure
  • RESTful Web Apps
  • Frameworks: Hibernate, Grails, Spring, JSF, GWT, more
  • Agility
  • Test Driven Design
  • Security
  • Ajax, Flex, RIA
View Event Details »

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 7
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 3
  • Automation Tools: Git, Hudson, Sonar
  • HTML5, Ajax, jQuery, Usability
  • Mobile Applications - iPhone and Android
  • More...
Learn More »

NFJS, the Magazine

May Issue Now Available
  • On the road to learning

    by Raju Gandhi
  • Refactoring to Modularity

    by Kirk Knoernschild
  • RESTful Groovy

    by Kenneth Kousen
  • Getting Started with D3.js

    by Brian Sletten
Learn More »