alt.lang.jre: Feeling Groovy

Introducing a new standard language for the Java platform

Whereas the Java language has won over an entire generation of programmers with its commitment to exactitude and extensiveness, Groovy heralds a new era of programming on the Java platform, one defined by convenience, expedience, and agility. In this second installment in the new alt.lang.jre column, Andrew Glover offers an informal introduction to the proposed addition to the standard programming languages for the Java platform.

Andrew Glover, CTO, Vanward Technologies

Andrew GloverAndrew Glover is the President of Stelligent Incorporated, a Washington, DC, metro area company specializing in the construction of automated testing frameworks, which lower software bug counts, reduce integration and testing times, and improve overall code stability.



03 August 2004

If you've been hanging out on the Java block for any amount of time then you've likely heard of Groovy. The brainchild of superstar developers James Strachan and Bob McWhirter, Groovy is an agile development language that is based entirely on the Java programming APIs. Groovy is currently in the beginning phase of its Java Specification Request, which was approved in late March of 2004. Groovy is also the scripting language that some claim will forever change the way that you view and utilize the Java platform.

In his opening comments to JSR 241 (see Resources) Groovy co-specification lead Richard Monson-Haefel said that he based his support for Groovy on the conviction that the time has come for the Java platform to include an agile development language. Unlike the many scripting languages that exist as ports to the Java platform, Groovy was written for the JRE. With the request for specification (see Resources), the makers of Groovy have put forth the idea that (in the words of Monson-Haefel) "Java is more than a programming language; it's a robust platform upon which multiple languages can operate and co-exist."

I'll devote this second installment of the new alt.lang.jre column to getting to know Groovy. I'll start with some answers to the most obvious question about this new language (why do you need it?), and then embark on a code-based overview of some of Groovy's most exciting features.

Why another language?

As you learned in last month's column, Groovy isn't the only scripting language that is compliant with the JRE. Python, Ruby, and Smalltalk are just three examples of scripting languages that have been successfully ported to the Java platform. For some developers, this begs the question: Why another language? After all, many of us already combine our Java code with Jython or JRuby for faster application development; why should you learn another language? The answer is that you don't have to learn a new language to code with Groovy. Groovy differentiates itself from the other JRE-compliant scripting languages with its syntax and reuse of standard Java libraries. Whereas Jython and JRuby share the look and feel of their ancestors (Python and Ruby, respectively), Groovy feels like the Java language with far fewer restrictions.

About this series

While most readers of this series are familiar with the Java language and how it runs on a cross-platform virtual machine, fewer may know that the Java Runtime Environment can host languages besides the Java language. This series of articles is a survey of many of the alternate languages for the JRE. Most of the languages explored here are open source and may be used for free, while a few are commercial products that must be purchased. All of the languages introduced in the alt.lang.jre series are supported by the JRE and are believed by the authors to enhance the dynamic and flexible nature of the Java platform.

Whereas languages like Jython build upon their parents' libraries, Groovy employs the features and libraries Java developers are most familiar with -- but puts them in an agile development framework. The fundamental tenets of agile development are that code should be well suited to a wide range of tasks and applicable in a variety of ways. Groovy lives up to these tenets by:

  • Freeing developers from compilation
  • Permitting dynamic types
  • Easing syntactical constructs
  • Allowing its scripts to be used inside normal Java applications
  • Providing a shell interpreter

These features make Groovy a remarkably easy language to learn and use, whether you're a seasoned Java developer or newcomer to the Java platform. In the sections that follow, I'll discuss the above mentioned highlights of Groovy in detail.


Look, Ma, no javac!

Like many scripting languages, Groovy saves compilation for runtime. This means that Groovy scripts are interpreted when they are run, much like JavaScript is interpreted by the browser when a Web page is viewed. Runtime evaluation comes at a cost in terms of execution speed, which could rule out the use of scripting languages in performance intensive projects, but compilation-free coding offers tremendous advantages when it comes to the build-and-run cycle. Runtime compilation makes Groovy an ideal platform for rapid prototyping, building various utilities, and testing frameworks.

The power of scripting

Scripting languages are popular because they are easy to learn and place few boundaries on developers. Scripting languages typically utilize a simple, rather terse syntax, which allows developers to create real-world applications with fewer lines of code than most programming languages require. Languages such as Perl, Python, Ruby, and now Groovy, take the art of programming to a new level of efficiency with their facile approaches to crafting code. This increased agility is usually associated with an increase in developer efficiency. The success of scripting languages demonstrates that scripting is not a niche technology or a hacker's pastime, but a viable technology employed by world-class companies such as Google, Yahoo, and IBM.

For example, running the script Emailer.groovyin Groovy is as easy as typing groovy Emailer.groovy on a command line. If you wanted to run the same Java file (Emailer.java) you would, of course, have to type an extra command: javac Emailer.java, followed by java Emailer. While this might seem trivial, you can easily imagine the advantage of runtime compilation in a larger context of application development.

As you will see shortly, Groovy also permits scripts to drop a main method in order to statically run an associated application.


The dynamic dynamo

As with other mainstream scripting languages, Groovy does not require the explicit typing of formal languages such as C++ and the Java language. In Groovy, an object's type is discovered dynamically at runtime, which greatly reduces the amount of code you have to write. You can see this, first, by studying the simple examples in Listings 1 and 2.

Listing 1 shows how you declare a local variable as a String in the Java language. Note that the type, name, and value must all be declared.

Listing 1. Java static typing
String myStr = "Hello World";

In Listing 2, you see the same declaration but without the need to declare the variable type.

Listing 2. Groovy dynamic typing
myStr = "Hello World"

You may have also noticed that I was able to drop the semicolon from the declaration in Listing 2. Dynamic types have dramatic consequences when defining methods and their associated parameters: Polymorphism takes on a whole new meaning! In fact, with dynamic typing, you can have all the power of polymorphism without inheritance. In Listing 3, you can really begin to see the role of dynamic typing in Groovy's flexibility.

Listing 3. More Groovy dynamic typing
class Song{
  length
  name
}

class Book{
  name
  author
}

def doSomething(thing){
  println "going to do something with a thing named = " + thing.name
}

Here, I've defined two Groovy classes, Song and Book, which I'll discuss further in a moment. Both classes contain a name property. I've also defined a function, doSomething, that takes a thing and attempts to print the object's name property.

Because the doSomething function does not define a type for its input parameter, any object will work so long as the object contains a name property. So, in Listing 4, you see what happens when you use both instances of Song and Book as input to doSomething.

Listing 4. Playing around with dynamic typing
mySong = new Song(length:90, name:"Burning Down the House")
myBook = new Book(name:"One Duck Stuck", author:"Phyllis Root")

doSomething(mySong) //prints Burning Down the House
doSomething(myBook) //prints One Duck Stuck

anotherSomething = doSomething

anotherSomething(myBook) //prints One Duck Stuck

In addition to demonstrating dynamic typing in Groovy, Listing 4 also reveals, in its last two lines, how easy it is to create a reference to a function. This is because everything in Groovy is an object, including functions.

The final thing you should note about Groovy's dynamic type declaration is that it results in fewer import statements. While Groovy does require imports for explicitly utilized types, those imports can be aliased to provide for shorter names.

Dynamic types roundup

The next two examples will pull together everything I've discussed so far about dynamic types in Groovy. Both the Java code group and the Groovy code group below make use of Freemarker (see Resources), an open source template engine. Both groups simply create a Template object from a directory and file name, then print the corresponding object's content to standard out; the difference, of course, is in the amount of code each group requires to handle its tasks.

Listing 5. Simple TemplateReader Java class
import java.io.File;
import java.io.IOException;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class TemplateReader {

  public static void main(String[] args) {
    try{
	  Configuration cfg = Configuration.getDefaultConfiguration();
	  cfg.setDirectoryForTemplateLoading(
	       new File("C:\\dev\\projects\\http-tester\\src\\conf"));
		    
	  Template temp = cfg.getTemplate("vendor-request.tmpl");
		
  	  System.out.println(temp.toString());
      }catch(IOException e){
        e.printStackTrace();
      }
  }
}

At first glance, the Java code in Listing 5 is quite simple -- especially if you've never seen scripting code before. Fortunately, you've got a Groovy contrast in Listing 6. Now this code is simple!

Listing 6. An even simpler TemplateReader in Groovy
import freemarker.template.Configuration as tconf
import java.io.File

cfg = tconf.getDefaultConfiguration()

cfg.setDirectoryForTemplateLoading(
  new File("C:\\dev\\projects\\http-tester\\src\\conf"))
  
temp = cfg.getTemplate("vendor-request.tmpl")

println temp.toString()

The Groovy code is half as long as the Java code; here's why:

  • The Groovy code requires half as many import statements. Notice also, that freemarker.template.Configuration was aliased to tconf enabling shorthand syntax.
  • Groovy permits the variable tmpl of type Template to drop its type declaration.
  • Groovy does not require a class declaration or a main method.
  • Groovy does not care about any corresponding exceptions, allowing you to drop the import of IOException required in the Java code.

Now, before you move on, think about the last Java class you coded. You probably had to write a lot of imports and declared types followed by an equal number of semicolons. Think about what it would be like to use Groovy to craft the same code. You'd have a far more concise syntax at your disposal, not so many rules to satisfy, and you'd end up with the exact same behavior.

And to think, you're just getting started ...


Extremely flexible syntax

When it comes to syntax, flexibility is the primary ingredient that lets you develop code more efficiently. Much like its influential counterparts (Python, Ruby, and Smalltalk), Groovy greatly simplifies the core library usage and constructs of the language on which it's modeled, which in this case is the Java language. To give you an idea of just how flexible Groovy's syntax is, I'll show you some of its primary constructs; namely classes, functions (via the def keyword), closures, collections, ranges, maps, and iterators.

Classes

At the bytecode level, Groovy classes are real Java classes. What's different, however, is that Groovy defaults everything defined in a class to public, unless a specific access modifier has been defined. Moreover, dynamic typing applies to fields and methods, and return statements are not required.

You can see an example of class definition in Groovy in Listing 7, where class Dog has a getFullName method that actually returns a String representing the Dog's fullname. All methods, consequently, are implicitly public.

Listing 7. Example Groovy class: Dog
class Dog{
  name

  bark(){
    println "RUFF! RUFF!"
  }
  
  getFullName(master){
    name + " " + master.lname
  }
  
  obeyMaster(){
    println "I hear you and will not obey."
  }
}

In Listing 8, you take things one step further, with a DogOwner class that has two properties, fname and lname. Simple so far!

Listing 8. Example Groovy class: DogOwner
class DogOwner{
  fname
  lname

  trainPet(pet){
    pet.obeyMaster()
  }
  
}

In Listing 9, you use Groovy to set properties and call methods on your Dog and DogOwner instances. It should be obvious, by now, how much easier it is to work with Groovy classes than with Java classes. While the new keyword is required, types are optional and setting properties (which are implicitly public) is quite effortless.

Listing 9. Using Groovy classes
myDog = new Dog()
myDog.name = "Mollie"

myDog.bark()
myDog.obeyMaster() 

me = new DogOwner()
me.fname = "Ralf"
me.lname = "Waldo"

me.trainPet(myDog)

str = myDog.getFullName(me)
println str  // prints Mollie Waldo

Notice how the getFullName method defined in your Dog class returns a String object, which in this case is "Mollie Waldo."

First class objects

A first class object is an object that can be created with data and utilized at runtime. First class objects can also be passed into and out of functions, stored as variables, and returned from other objects. Primitive data types that are native to the Java language, such as int and boolean, are not considered first class objects. Many object-oriented purists lament this small detail, and some have used it to raise the question of whether the Java language is truly an object-oriented language. Groovy resolves this problem by declaring that everything is an object.

Defs

In addition to designating all objects as first class, which many scripting languages do (see sidebar), Groovy also lets you create first class functions, which are, in essence, objects themselves. These are declared with the def keyword and exist outside a class definition. You've actually already seen how you use the def keyword to define a first class function, in Listing 3 and seen a function used in Listing 4. Groovy's first class functions are extremely handy when it comes to defining simple scripts.

Closures

One of the most exciting and powerful features found in Groovy is its support for closures. Closures are first class objects that are similar to anonymous inner classes found in the Java language. Both closures and anonymous inner classes are executable blocks of code; however there are some subtle differences between the two. State is automatically passed in and out of closures. Closures can have names. They can be reused. And, most important and true to Groovy, closures are infinitely more flexible than anonymous inner classes!

Listing 10 demonstrates just how powerful closures are. The new and improved Dog class in the listing includes a train method that actually executes the closure with which the Dog instance was created.

Listing 10. Using closures
class Dog{  
  action

  train(){
    action.call()
  }
}

sit = { println "Sit, Sit! Sit! Good dog"}
down = { println "Down! DOWN!" }


myDog = new Dog(action:sit)
myDog.train()  // prints Sit, Sit! Sit! Good dog

mollie = new Dog(action:down)
mollie.train() // prints Down! DOWN!

What's more, closures can also accept parameters. As demonstrated in Listing 11, the postRequest closure accepts two parameters (location and xml) and uses the Jakarta Commons HttpClient library (see Resources) to post an XML document to a given location. The closure then returns a String representing the response. Below the closure definition is an example of using it; in fact, calling a closure is just like calling a function.

Listing 11. Using closures with parameters
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.PostMethod

postRequest = { location, xml |

  clint = new HttpClient()
  mthd = new PostMethod(location)  	
  mthd.setRequestBody(xml)
  mthd.setRequestContentLength(xml.length())
  mthd.setRequestHeader("Content-type", 
     "text/xml; charset=ISO-8859-1")
	
  statusCode = clint.executeMethod(mthd)
  responseBody = mthd.getResponseBody()
  mthd.releaseConnection()	
  return new String(responseBody)	  
}

loc = "http://localhost:8080/simulator/AcceptServlet/"
vxml = "<test><data>blah blah blah</data></test>"

str = postRequest(loc, vxml)
println str

Autoboxing

Autoboxing or boxing conversion is the process of automatically converting primitive data types such as ints, doubles, and booleans to their corresponding wrapper types, found in the java.lang package. This feature is found in J2SE 1.5 and frees developers from having to hand-code the conversions in source code.

Collections

Grouping objects into data structures such as lists and maps is a fundamental coding task, something most of us do on a daily basis. Like most languages, Groovy has defined a rich library to manage these types of collections. If you've ever dabbled in Python or Ruby, Groovy's collections syntax should be familiar. Creating a list is quite similar to creating an array in the Java language, as shown in Listing 12. (Notice how the list's second item is automatically autoboxed into an Integer type.)

Listing 12. Using collections
collect = ['groovy', 29, 'here', 'groovy']

In addition to making lists easier to work with, Groovy adds a few new methods on collections. These methods make it possible, for example, to count occurrences of values; join an entire list together; and sort a list with the greatest of ease. You can see these collections methods in action in Listing 13.

Listing 13. Working with Groovy collections
aCollect = [5, 9, 2, 2, 4, 5, 6] 

println aCollect.join(' - ')  // prints 5 - 9 - 2 - 2 - 4 - 5 - 6
println aCollect.count(2)     // prints 2
println aCollect.sort()       // prints [2, 2, 4, 5, 5, 6, 9]

Maps
Like lists, maps are data structures that are remarkably easy to work with in Groovy. The map in Listing 14 contains two objects, the keys being name and date. Notice that you can access the values in different ways.

Listing 14. Working with maps
myMap = ["name" : "Groovy", "date" : new Date()]

println myMap["date"]

println myMap.date

Ranges
When working with collections, you'll likely find yourself making ample use of Ranges. A Range is actually an intuitive notion and easy to pick up, as it allows one to create a list of sequential values inclusively or exclusively. You use two dots (..) to declare an inclusive range and three dots (...) to declare an exclusive one, as shown in Listing 15.

Listing 15. Working with ranges
myRange = 29...32
myInclusiveRange = 2..5

println myRange.size() // prints 3
println myRange[0]   // prints 29
println myRange.contains(32) //prints false

println myInclusiveRange.contains(5) //prints true

Looping with ranges
Ranges allow for a rather slick notion when it comes to looping constructs. In Listing 16, with aRange defined as an exclusive range, the loop will print a, b, c, and d.

Listing 16. Looping with ranges
aRange = 'a'...'e'

for (i in aRange){
  println i
}

Additional features of collections
If you're unfamiliar with Python and other scripting languages, then some of the additional features found in Groovy's collections will impress you. For example, once you've created a collection, you can use negative numbers to count backwards in a list, as shown in Listing 17.

Listing 17. Negative indexing
aList = ['python', 'ruby', 'groovy']

println aList[-1] // prints groovy
println aList[-3] // prints python

Groovy also allows you to slice lists using ranges. Slicing lets you obtain a precise subset of a list, as demonstrated in Listing 18.

Listing 18. Slicing with ranges
fullName = "Andrew James Glover"

mName = fullName[7...13]

print "middle name: " + mName // prints James

Collections ala Ruby
Groovy collections can also act like Ruby collections, if you want them to. You can use Ruby-like syntax to append elements with the << syntax, concatenate with +, and perform set difference on collections with -; moreover, you can handle repetition of collections with the * syntax as shown in Listing 19. Also note that you can use == to compare collections.

Listing 19. Ruby-style collections
collec = [1, 2, 3, 4, 5]
collec << 6 //appended 6 to collec

acol = ['a','b','c'] * 3 //acol now has 9 elements

coll =  [10, 11]
coll2 = [12, 13]

coll3 = coll + coll2 //10,11,12,13

difCol = [1,2,3] - [1,2] //difCol is 3

assert [1, 2, 3] == [1, 2, 3] //true

Iterators

In Groovy, it's quite easy to iterate over any sequence. All you need to iterate over the sequence of characters is a simple for loop, as shown in Listing 20. (As you may also have noticed by now, Groovy provides a much more natural for loop syntax than traditional pre Java 1.5.)

Listing 20. Iterator example
str = "uncle man, uncle man"

for (ch in str){
  println ch
}

Most objects in Groovy have methods such as each and find that accept closures. Using closures to iterate over objects creates a number of exciting possibilities, as demonstrated in Listing 21.

Listing 21. Closures with iterators
[1, 2, 3].each {  
  val = it 
  val += val
  println val
}

[2, 4, 6, 8, 3].find { x |
     if (x == 3){
       println x
     }
}

In Listing 21, the method each acts as an iterator. In this case, your closure adds the values of the elements, leaving val at 6 when complete. The find method is fairly simple, too. Each iteration passes in the element. In this case, you simply test to see if the value is 3.


Groovy for graduates

So far I've focused on the basic aspects of working with Groovy, but there's far more to this language than the basics! I'll wrap up with a look at some of the more advanced development features Groovy has to offer, including Groovy-style JavaBeans components, file IO, regular expressions, and compilation with groovyc.

GroovyBeans!

Invariably, applications end up employing struct-like objects representing real world entities. On the Java platform, you call these objects JavaBeans components, and they're commonly used as business objects representing orders, customers, resources, etc. Groovy simplifies the coding of JavaBeans components with its handy shorthand syntax, and also by automatically providing a constructor once you've defined the properties of a desired bean. The result, of course, is greatly reduced code, as you can see for yourself in Listing 22 and 23.

In Listing 22 you see a simple JavaBeans component that has been defined in the Java language.

Listing 22. A simple JavaBean component
public class LavaLamp {
  private Long model;
  private String baseColor;
  private String liquidColor;
  private String lavaColor;

  public String getBaseColor() {
    return baseColor;
  }

  public void setBaseColor(String baseColor) {
    this.baseColor = baseColor;
  }

  public String getLavaColor() {
    return lavaColor;
  }

  public void setLavaColor(String lavaColor) {
    this.lavaColor = lavaColor;
  }

  public String getLiquidColor() {
    return liquidColor;
  }

  public void setLiquidColor(String liquidColor) {
    this.liquidColor = liquidColor;
  }

  public Long getModel() {
    return model;
  }

  public void setModel(Long model) {
    this.model = model;
  }
}

In Listing 23, you see what happens when this bean gets Groovy. All you have to do is define your properties, and Groovy automatically gives you a nice constructor to work with. Groovy also gives you quite a bit of flexibility when it comes to manipulating instances of LavaLamp. For instance, we can use Groovy's shorthand syntax or the traditional wordy Java language syntax to manipulate the properties of your bean.

Listing 23. A Groovier JavaBeans component
class LavaLamp{
  model
  baseColor
  liquidColor
  lavaColor
}

llamp = new LavaLamp(model:1341, baseColor:"Black", 
  liquidColor:"Clear", lavaColor:"Green")

println llamp.baseColor
println "Lava Lamp model ${llamp.model}"

myLamp = new LavaLamp()
myLamp.baseColor = "Silver"
myLamp.setLavaColor("Red")

println "My Lamp has a ${myLamp.baseColor} base"
println "My Lava is " + myLamp.getLavaColor()

Breezy IO

Groovy IO operations are a breeze, especially when combined with iterators and closures. Groovy takes standard Java objects such as File, Reader, and Writer and enhances them with additional methods that accept closures. In Listing 24, for example, you see the traditional java.io.File, but with the addition of the handy eachLine method.

Listing 24. Groovy IO
import java.io.File

new File("File-IO-Example.txt").eachLine{ line |
 println "read the following line -> " + line
}

Because a file is essentially a sequence of lines, characters, etc., you can iterate over them quite simply. The eachLine method accepts a closure and iterates over each line of the file, in this case File-IO-Example.txt. Using closures in this manner is quite powerful, because Groovy ensures all file resources are closed, regardless of any exceptions. This means you can do file IO without lots of try/catch/finally clauses!

Advanced compilation

Groovy scripts are actually Java classes on the byte code level. As a result, you can easily compile a Groovy script using groovyc. groovyc can be utilized via the command line or Ant to produce class files for scripts. These classes can be run with the normal java command, provided that the classpath includes groovy.jar and asm.jar, which is ObjectWeb's bytecode manipulation framework . See Resources to learn more about compiling Groovy.

Maximum RegEx

No language would be worth its salt without regular expression handling. Groovy uses the Java platform's java.util.regex library -- but with a few essential tweaks. For example, Groovy lets you create Pattern objects with the ~ expression and Matcher objects with the =~ expression, as shown in Listing 25.

Listing 25. Groovy RegEx
str =  "Water, water, every where,
        And all the boards did shrink;
        Water, water, every where,
        Nor any drop to drink."

if (str =~ 'water'){
  println 'found a match'
}

ptrn = ~"every where"

nStr = (str =~ 'every where').replaceAll('nowhere')

println nStr

You may have noticed that you were able to define the String, str in the above listing, without having to add end quotes and +'s for each new line. This is because Groovy has relaxed the normal Java constraints that would have required multiple string concatenations. Running this Groovy script will print true for your match of water and then print out a stanza with all occurrences of "every where" replaced with "nowhere."

Hop on the (band)shell

Groovy offers two different interpreters that allow any valid Groovy expression to be executed interactively. These shells are extremely powerful mechanisms with which to quickly learn Groovy.


Conclusion

Like any project in its infancy, Groovy is a work in progress. Developers accustomed to working with Ruby and Python (or Jython) might miss the convenience of such features as mixins, script import (although it's possible to compile the desired importable script into its corresponding Java class), and named parameters for method calls. But Groovy is definitely a language on the move. It will likely incorporate these features and more as its developer base grows.

In the meantime, Groovy has a lot going for it. It nicely blends some of the most useful features of Ruby, Python, and Smalltalk, while conserving a core syntax based on the Java language. For developers familiar with the Java platform, Groovy provides a simpler alternative with almost no ramp-up time. For developers new to the Java platform, it may act as an easy entry point to the more extensive syntax and requirements of the Java language.

Like the other languages discussed in this series, Groovy is not a replacement for the Java language but an alternative to it. Unlike the other languages discussed here, Groovy is seeking Java specification, which means it has the potential to share equal footing with the Java language on the Java platform.

In this month's installment of alt.lang.jre, you've learned about the basic framework and syntax of Groovy, along with some of its more advanced programming features. Next month will be devoted to one of the most well-loved scripting languages for Java developers: JRuby.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=15002
ArticleTitle=alt.lang.jre: Feeling Groovy
publish-date=08032004