A Stroll with Groovy (part II) : Groovy Has Class

In part I we explored Groovy by writing a simple script that approximated the value of PI using the Monte Carlo method. In  groovy_mediumthis installment I want to explore creating classes in Groovy. We'll refactor our script and move some of the logic that lends itself to parallel processing into a simple Groovy class.

Here's our original groovy script:

package com.appistry.demo.groovy 
 
totalPoints = 500000 
generator = new Random(System.currentTimeMillis()) 
pointsInCircle = 0 
 
<font color="#ff0000">totalPoints.times { 
  double x = generator.nextDouble() 
  double y = generator.nextDouble() 
  if (Math.sqrt(x * x + y * y) &lt;= 1) 
    pointsInCircle++ 
}</font> 
 
pi = 4.0 * pointsInCircle / totalPoints 
 
println &quot;\nApproximating PI using Monte Carlo Method&quot; 
println &quot;Points Attempted : &quot; + totalPoints 
println &quot;Points in Circle : &quot; + pointsInCircle 
println &quot;  Approximate PI : &quot; + pi

The point generation loop (marked in red) is the target of our refactoring. We can dole that logic out to multiple cores and/or multiple machines to efficiently generate a lot  of points in parallel if we want to. So we'll start simply and pull that loop out into a Groovy class. We'll have to bring along some variables also. Here's our new PointGenerator class:

package com.appistry.demo.groovy
 
class PointGenerator {
 
  static computePoints(totalPoints)
  {
    def x, y
    def pointsInCircle = 0
    def generator = 
      new Random(System.currentTimeMillis())
 
    totalPoints.times {
      x = generator.nextDouble()
      y = generator.nextDouble()
      if (Math.sqrt(x * x + y * y) &lt;= 1)
        pointsInCircle++
    }
    return pointsInCircle 
  }
}

So what did we learn?

  • As mentioned last time, Groovy maps to Java's package scheme, and I'm choosing to use it here.
  • We define a public class PointGenerator. It has no constructor. This simple class doesn't need one, so we'll get off into Groovy's constructors at a later time.
  • We define a static method computePoints which takes totalPoints. Groovy classes also support non-static methods and member variables of course, but we don't have any interesting ones yet.
  • Groovy supports both duck typing and strong typing of data. Throughout our new method, I've not specified datatypes explicitly. Groovy gives you the flexibility of stating a type if you wish. I could have restricted x and y down to doubles. Indeed, if you look back at my original script, I did just that. In the case of the class, I used the "def" directive to declare x and y. Nominally, to a Java developer, "def" is like declaring "Object" to declare a variable, and this is where Groovy's duck typing support comes in. 
  • One last note on "def," you may have noticed that in the script, totalPoints is initialized without using def, whereas in my class method I used it. That's because in the class definition, def is required.
  • Like Ruby and other dynamic languages, a Groovy method returns the last evaluated expression as its return value, unless there is an explicit return statement. In our case, the last evaluated expression, I believe, is totalPoints.times which returns null. So, we explicitly return pointsInCircle.

Now that we've moved that logic into a class, here's our reworked script (with the changed line in red) using the new Groovy class.

package com.appistry.demo.groovy
 
totalPoints = 500000
<font color="#ff0000">pointsInCircle = PointGenerator.computePoints(totalPoints)</font>
pi = 4.0 * pointsInCircle / totalPoints
 
println &quot;\nComputing PI using Monte Carlo Method&quot;
println &quot;Points Attempted : &quot; + totalPoints
println &quot;Points in Circle : &quot; + pointsInCircle
println &quot;  Approximate PI : &quot; + pi

We'll save the modified script off to compute_pi2.groovy and give it a spin.

$ groovy compute_pi2.groovy
Caught: groovy.lang.MissingPropertyException: 
     No such property: PointGenerator for class: 
     com.appistry.demo.groovy.compute_pi2
     at com.appistry.demo.groovy.compute_pi2.main(compute_pi2.groovy)

Whoops, no joy. Groovy can't find our new PointGenerator class that the script uses. We need to compile our Groovy class first.

$ groovyc PointGenerator.groovy
 
$

If you poke around now, you'll see a new package directory structured created in the current directory. You'll see two files down there:

$ ls com/appistry/demo/groovy/
PointGenerator$_computePoints_closure1.class  PointGenerator.class

The PointGenerator.class is our Groovy class compiled down to a regular Java class file. The word "closure" imbedded in the name PointGenerator$_computePoints_closure1.class tells us a little bit about how closures are handled in Groovy. Incidently, the Groovy homepage has lots of information about closures and the advantages they give over Java's anonymous inner classes.

Okay, let's try it again:

$ groovy compute_pi2.groovy
 
Computing PI using Monte Carlo Method
Points Attempted : 500000
Points in Circle : 392558
  Approximate PI : 3.140464

Okay, so I've learned a bit about creating classes in Groovy. There's still a lot of unexplored territory around Groovy classes, constructors, and scoping, and what not, all of which is covered by the excellent examples and docs on the Groovy homepage. I'll leave that to those that want to dig further.

Next time I'll hare off in a different direction with Groovy.

Post new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
1 + 4 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.