My friend and colleage Alex Garrett, just posted Groovy coding a pile of dirt.
Now, I thought his example could be improved, and came up with an example making use of Java’s enums, which seemed a nice fit:
enum Material {
CEMENT(131.0F, 20.0F),
DRYSAND(90.0F, 35.0F),
WETSAND(118.0F, 25.0F),
CLEANGRAVEL(118.0F, 37.5F),
SANDYGRAVEL(118.0F, 27.5F)
Material(D, thetaDegrees) {
this.D = D
this.theta = Math.toRadians(thetaDegrees)
}
final D
final theta
}
class ConicalPile {
private m
private h
def getWeightPounds() {
(Math.PI * h ** 3 * m.D) / (3 * (Math.cos(m.theta) ** 2))
}
def getWeightTons() {
getWeightPounds() / 2000.0F
}
@Override
public String toString() {
"""$m
Weight(pounds): ${String.format("%.3f", getWeightPounds())}
Weight(tons): ${String.format("%.3f", getWeightTons())}"""
}
}
for (Material m : Material.values())
println new ConicalPile(m: m, h: 10.0F)
Easy! Took 5 minutes. A nice easy diversion.
Not so fast there, cowboy…
That was the finished product…took me a bit of effort to get to it.
To see why, here’s a simplified test application:
enum T {
ALPHA(1, 2),
BETA(3, 4),
GAMMA(5, 6)
def x
def y
@Override
public String toString() {
"{x: $x, y: $y}"
}
}
println T.ALPHA
Didn’t work! Imagine my surprise! Gives:
Caught: java.lang.ExceptionInInitializerError at Test.class$(Test.groovy) at Test.$get$$class$T(Test.groovy) at Test.run(Test.groovy:20)
So (on the assumption that enums are just specialised classes: The new enum declaration defines a full-fledged class (dubbed an enum type)) I tried a Groovy-style constructor with named parameters:
enum T {
ALPHA(x: 1, y: 2),
BETA(x: 3, y: 4),
GAMMA(x: 5, y: 6)
def x
def y
@Override
public String toString() {
"{x: $x, y: $y}"
}
}
println T.ALPHA
Didn’t work. One gets an almighty compiler error:
Information:Compilation completed with 1 error and 0 warnings Information:1 error Information:0 warnings Error:BUG! exception in phase 'class generation' in source unit 'C:UsersBobDesktopAlexMaterialsrcTest.groovy' MapEntryExpression should not be visited here at org.codehaus.groovy.classgen.AsmClassGenerator.visitMapEntryExpression(AsmClassGenerator.java:3058)at org.codehaus.groovy.ast.expr.MapEntryExpression.visit(MapEntryExpression.java:37)at org.codehaus.groovy.classgen.AsmClassGenerator.visitAndAutoboxBoolean(AsmClassGenerator.java:4029)at org.codehaus.groovy.classgen.AsmClassGenerator.makeCallSite(AsmClassGenerator.java:1955)at org.codehaus.groovy.classgen.AsmClassGenerator.makeCall(AsmClassGenerator.java:1789)at org.codehaus.groovy.classgen.AsmClassGenerator.makeCall(AsmClassGenerator.java:1775)at org.codehaus.groovy.classgen.AsmClassGenerator.visitStaticMethodCallExpression(AsmClassGenerator.java:2334)at org.codehaus.groovy.ast.expr.StaticMethodCallExpression.visit(StaticMethodCallExpression.java:43)at org.codehaus.groovy.classgen.AsmClassGenerator.visitAndAutoboxBoolean(AsmClassGenerator.java:4029)at org.codehaus.groovy.classgen.AsmClassGenerator.visitCastExpression(AsmClassGenerator.java:1701)at org.codehaus.groovy.classgen.AsmClassGenerator.assignmentCastAndVisit(AsmClassGenerator.java:3968)at org.codehaus.groovy.classgen.AsmClassGenerator.evaluateEqual(AsmClassGenerator.java:3920)at org.codehaus.groovy.classgen.AsmClassGenerator.visitBinaryExpression(AsmClassGenerator.java:1324)at org.codehaus.groovy.ast.expr.BinaryExpression.visit(BinaryExpression.java:49)at org.codehaus.groovy.classgen.AsmClassGenerator.visitAndAutoboxBoolean(AsmClassGenerator.java:4029)at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:1305)at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:40)at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:38)at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:129)at org.codehaus.groovy.classgen.AsmClassGenerator.visitBlockStatement(AsmClassGenerator.java:665)at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:52)at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:73)at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:80)at org.codehaus.groovy.classgen.AsmClassGenerator.visitStdMethod(AsmClassGenerator.java:542)at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:518)at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:88)at org.codehaus.groovy.classgen.AsmClassGenerator.visitMethod(AsmClassGenerator.java:622)at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1004)at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:48)at org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:243)at org.codehaus.groovy.control.CompilationUnit$10.call(CompilationUnit.java:717)at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:924)at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:462)at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:443)at org.jetbrains.groovy.compiler.rt.MyCompilationUnits.compile(MyCompilationUnits.java:52)at org.jetbrains.groovy.compiler.rt.GroovycRunner.main(GroovycRunner.java:188)
So much for guessing, which is essentially what I was doing.
So, back to a simpler, kinder Java-style, with a hand-crafted constructor (which I first tried as a pure Java class, and then ‘mutated’ back to Groovy):
enum T {
ALPHA(1, 2),
BETA(3, 4),
GAMMA(5, 6)
T(x, y) {
this.x = x
this.y = y
}
def x
def y
@Override
public String toString() {
"{x: $x, y: $y}"
}
}
println T.ALPHA
It flies! Viz:
{x: 1, y: 2}
Hence the example I lead with…
I’m assuming that I was being bitten by http://jira.codehaus … g/browse/GROOVY-3276, which is currently marked as Closed/Fixed. Hmmm.
##Hi ho. Hi ho. It’s off to Jira I go…##
[edit]
As has been pointed out to me:
Your surprise #1 does not make much sense to me because even with regular classes, that convenience is not there.
class T { def x def y public String toString() { "{x: $x, y: $y}" } }new T(1, 2) // fails saying no matching constructor found
I got my knickers knotted on this issue, it is true! Still:
I would have also assumed that named parameters based constructor call would go through for enums as it does for regular classes. But that is about your 2nd surprise reported. To me also, that enhancement would make sense.
