Nyheterna i Groovy

Efter nyåret släpptes lanseringskandidaten av Groovy 2.1 och går allt vägen, så har vi den skarpa versionen i slutet av januari. Groovy 2.x bjuder på många nyheter och förbättrar språket både vad gäller funktionalitet och prestanda.

Men hallå! Vad är Groovy?

Enkelt uttryckt är det Java++, en bättre variant av Java helt enkelt. Ungefär som C++ är en bättre variant av C. Det går att blanda Java och Groovy hur enkelt som helst. Tack vare den familjära syntaxen går rekord-snabbt för en Java programmerare att börja använda Groovy och det är mycket enkelt att smyga in lite Groovy här och där i en befintlig Java applikation.

Språket Groovy genomgår just nu en mycket intressant vidare-utveckling, som började för lite drygt ett och ett halvt år sedan med version 1.8. Denna version var knökfull av nya egenskaper och när språkets utvecklingsteam planerade för version 1.9 insåg de vad de borde ha gjort redan för version 1.8, nämligen att börja på nästa major version, dvs 2.x. Så 1.9 blev helt sonika omdöpt till 2.0 och på den vägen är det.

AST Annotations

Den mest iögonfallande nyheten i version 1.8 var möjligheten att generera ny kompilerad kod i samband med att ett Groovy program startar. Detta kallas för AST Transformations, där AST betyder Abstract Syntax Tree. I (mycket) korta drag så när ett Groovy program laddas för exekvering, så kompileras det först till Java byte-code. Under denna process så byggs det upp en intern trädrepresentation av programkoden (AST), som i flera olika steg transformeras.

Med AST-T kan man lägga till egna transformationer, dvs dynamiskt skapa ny kod, som kommer med i kompileringen. Nu är detta inget man gör i den dagliga programmeringen, utan man använder färdiga transformationer, som finns tillgängliga i form av kod-annoteringar. Genom att applicera en eller flera AST annoteringar, så utökas en Groovy klass med kompilerad kod. Låt oss kika på några exempel, intressanta för varje Java programmerare.

@ToString

Hur många gånger har man inte “mekaniskt” skrivit en toString() method i Java och för varje klass-ändring så måste denna uppdateras eller genereras på nytt. Det finns alternativ; såsom Jakarta Commons Lang ToStringBuilder, som dynamiskt (reflektivt) skapar output. Emellertid, om du använder Groovy i stället, så räcker det med att annotera din klass med @ToString, så är saken biff.

import groovy.transform.*
@ToString class Person {
String first, last
int age
}

def p1 = new Person(first:'Anna', last:'Conda', age:17)
println "p1 = ${p1}"

Vilket ger utskriften

p1 = Person(Anna, Conda, 17)

Vill man se attributnamnen också, lägger man till @ToString(includeNames=true) och utskriften blir i stället

p = Person(first:Anna, last:Conda, age:17)

@EqualsAndHashCode

En annan trist uppgift i Java, är att underhålla metoderna hashCode() och equals(). De behöver implementeras båda två och är viktiga för att ens objekt ska kunna förekomma i olika typer av list:or och map:ar. Med Groovy, så lägger du bara till @EqualsAndHashCode och kan gå vidare med mer intressant programmeringsuppgifter. Så här kan det se ut

import groovy.transform.*
@ToString(includeNames=true)
@EqualsAndHashCode
class Person {
String first, last
int age
}
//...
def p2 = new Person(first:'Per', last:'Silja')
def p3 = new Person(first:'Per', last:'Silja')

assert p2 == p3
assert p1 != p3
assert p2.hashCode() == p3.hashCode()

@Log4j

Loggning är ett annat område av rutin-programmering och innebär att man typiskt i en konstruktor skapar ett log objekt, lite olika beroende på om man använder Log4J, Commons-Logging, SLF4J eller java.util.logging. Varför inte låta en annotering sköta om detta i stället. Här ett exempel för Log4J.

@Log4j class Controller {
Controller() {
log.debug("Created");
}
void doit() {
log.info("Calling doit()");
//...
}
}

Och så vidare…

Det finns många fler AST annoteringar, såsom @AutoExternalizable som bygger writeExternal() och readExternal() method i gränssnittet Externalizable (vanlig optimering), @Immutable som gör en klass read-only, @Singleton som implementerar singleton-mönstret, och mång fler.

Exkvera snabbare

Det hur många som helst andra nyheter i Groovy från versionerna 1.8, 2.0 och 2.1, men jag tänkte uppehålla mig kring området prestanda. Groovy är ju ett dynamiskt programspråk, med bl.a. s.k. duck-typing. Det här innebär att en hel del kontroller och omvandlingar sker under exekveringen, vilket ibland tar lite för lång tid. Därför har prestanda-förbättringar av språket varit högt prioriterat av användarna. Det finns två parallella spår för att möta detta.
Det första spåret är att ge programmeraren mer kontroll över var kompilatorn kan generera byte-code, som har samma prestanda som motsvarande Java-kod. Det här görs med annoteringar.

@CompileStatic

Med annoteringen @CompileStatic markerar man att en metod eller klass inte behöver det “dynamiska” stödet utan resultatet blir Java-style byte-code.

@TypeChecked

Med annoteringen @TypeChecked markerar man att en metod eller klass ska kompileras med statisk typ-kontroll, vilket innebär att problem som kanske dyker upp först under exekveringen kan hittas redan vid kompileringen.
Med båda dessa annoteringar, så har man det bästa från två världar Java statiska typ-kontroll och prestanda, samt Groovy’s dynamiska beteende där man behöver det.

Invoke Dynamic

Det andra spåret är att använda det nya stödet i Java 7 för dynamiska språk (byte-code instruktionen invokeDynamic). Detta arbete påbörjades redan i Groovy 2.0, men väntas vara mer genomarbetat och tillgängligt i nya versionen 2.1.
I korta drag, så exekverar man ett Groovy program med den nya switchen --indy och ser till att Groovy använder Java 7 i botten.

groovy --indy myCoolApp.groovy

Mer om Groovy

Vecka 5

Under vecka 5 kommer jag tillbaka och bloggar hela veckan om Groovy relaterade saker.

Concurrency for the Modern Age

Om du inte har fått nog efter den veckan, så kommer jag att hålla ett frukost-seminarium den 21 februari om hur man med Groovy och GPars biblioteket kan skriva applikationer, som enkelt och bekvämt utnyttjar moderna datorers förmåga att exekver många aktiviteter parallellt (multi-cpu/core/threading).

Läs hela agendan och anmäl dig här