Man braucht kein Kognitionswissenschaftler zu sein, um zu erkennen, wie wichtig die Darstellung eines Sachverhalts für seine Verständlichkeit ist. Wie sähe unsere Welt wohl heute aus, wenn wir Zahlen nicht in arabischen, sondern nur in römischen Ziffern darstellen könnten? 42 und XLII tragen beide die gleiche Information, aber schon der Versuch, den Wert zu verdoppeln, zeigt den Unterschied. 84 lässt sich direkt aus der Ziffernfolge erschließen, nicht aber LXXXIV. Selbst die einfachsten Berechnungen wären so mühselig, dass jeglicher technologischer Erkenntnisfortschritt massiv behindert wäre.
Zugegeben, nicht ganz so dramatisch, aber doch merklich ist die Vereinfachung der Notation, die Groovy im Vergleich zu Java bringt. So wird zum Beispiel aus dem klassischen Java-Code
public class Hallo {public static void main(String[] args) {System.out.println("Hallo liebe JavaMagazin Leser!");}}
Viele Syntaxelemente, die in Java verlangt sind, sind in Groovy optional:
- Semikolon als Anweisungsbegrenzer
- Klammern um nicht leere Argumentlisten bei Top-Level-Ausdrücken
- explizite return-Anweisung
Das Erbe von Java
In den grundlegenden Konzepten folgt Groovy dem Java-Vorbild. Kommentare, Pakete, Imports, Klassen, Methoden, Felder und Initialisierungsblöcke sind in ihrer einfachsten Erscheinungsform identisch mit Java. Für Imports gibt es zusätzlich ein Type Aliasing mit dem as Schlüsselwort. Methodenparameter können auch ohne statischen Typ deklariert werden. Parameter werden optional, wenn ihnen mit = ein Standardwert zugewiesen wird. Durch Angabe des Typs Object[] werden Parameterlisten variabler Länge spezifiziert.Der Typ eines Feldes, des Rückgabewertes einer Methode oder einer lokalen Variablen kann wie in Java angegeben werden. Ersatzweise setzt das Schlüsselwort def die dynamische Typisierung in Kraft. Sogar def kann ausgelassen werden, wenn schon ein modifier die Deklaration als solche kenntlich macht.
class Hallo {static main(args) {println "So geht's auch."println sum(1,1)println sum('x','y','z')}static sum(a, b, c=0) {a + b + c}}
Die Schlüsselworte static, final, public, private, protected und synchronized werden mit leichten Abweichungen wie in Java verwendet. Die Standardsichtbarkeit von Klassen und Methoden ist public und wenn für Felder keine Sichtbarkeit spezifiziert wird, werden sie als Properties behandelt. Properties bestehen aus einem privaten Feld und zugehörigen getter- und setter-Methoden. Diese Methoden werden implizit im Bytecode generiert. Man kann sie jedoch überscheiben, indem man sie explizit ausprogrammiert. Für finale Properties werden sinnvollerweise keine setter-Methoden erzeugt.
class Spieler {String namedef nummer, mannschaft}
Vom Umgang mit Bohnen
Wenn wie im obigen Beispiel Bean-konforme Zugriffsmethoden verfügbar sind, bietet Groovy die Möglichkeit, auf die Properties in vereinfachter Notation zuzugreifen.def spieler = new Spieler()spieler.name = "Poldi"println spieler.name
Anwendung für XML
Der aufmerksame Java Magazin-Leser (und das sind ja alle) mag sich bei der Form dieser Ausdrücke an E4X erinnern [3]. In der Tat erlaubt Groovy eine ganz ähnliche Zugriffsform auf XML Daten.def text = "<mannschaft><spieler name='Peer'/><spieler name='Olli'/></mannschaft>"def mannschaft = new XmlParser().parseText(text)println mannschaft.spieler.'@name'
Der Attributzugriff "@name" wird übrigens deshalb in Hochkommata gestellt, um ihn von einem Feldzugriff zu unterscheiden. Er wird damit zu einem Property-Zugriff, und dass Properties, die mit dem @-Zeichen beginnen, auf den Attributzugriff abgebildet werden, ist eine Konvention, die speziell für „Knoten“-Konstruktionen à la DOM gilt.
Abläufe flexibel steuern
Die von Java bekannten Mechanismen zur Ablaufsteuerung wie if, while, switch, return, break, continue, try, catch, finally, synchronized haben auch in Groovy ihren Platz, sind jedoch deutlich flexibler. Boolesche Tests werden anhand der „Groovy Truth“ ausgewertet. Zu false evaluieren z.B.: null, 0, leere Strings, leere Collections, leere Maps und fehlschlagende Matcher regulärer Ausdrücke.Ein Groovy switch kann anhand beliebiger Kandidatenobjekte operieren, wobei jeder case-Teil einen Klassifikator benutzt. Als Klassifkator kann jedes Objekt dienen, dass die Methode Boolean isCase(kandidat) implementiert.
switch(x) {case "cool" : println "switch is cool!"breakcase String : println "x ist ein String"breakdefault : println "x ist etwas anderes"}
Groovys for-Schleife folgt der Form for(identifier in iterable){...} und ist der neuen Schleife aus Java 5 äußerlich ähnlich. In Groovy kann jedoch jedes beliebige Objekt als iterable verwendet werden: Iterators, Enumerations, Collections, Maps, Files, Ranges, aber auch Exoten wie eine org.w3c.dom.NodeList oder eine Methodenreferenz.
File datei = new File("/etc/passwd")for (zeile in datei) {println zeile}
Zu guter Letzt hat auch assert Einfluss auf die Ablaufsteuerung. Groovy hat gegenüber Java hier den Vorteil, dass man Assertions nicht abschalten kann. Man kann sich auf ihre Ausführung verlassen. Optionale Meldungen werden nicht mit Doppelpunkt, sondern mit Komma angefügt. assert schokoriegel, "Ohne Verpflegung gehe ich nicht weiter!"
Operatoren selbst gemacht
Groovy operiert ausschließlich auf Objekten, nicht auf primitiven Datentypen. So ist 1 kein int, sondern ein java.lang.Integer und in 1+2 muss der plus-Operator auf diesem Objekt aufgerufen werden. Das geschieht durch den Aufruf der plus-Methode, die Groovy den Nummerntypen hinzufügt. Entsprechende Methoden gibt es für alle Operatoren. Die lange Liste findet sich bei [2].Das Schöne ist, dass man als Programmierer bei diesem Spiel mitmachen kann, indem man einfach die entsprechende Methode implementiert.
class Euro {def mengedef plus(Euro e) {new Euro(menge: menge + e.menge)}boolean equals(e) {menge == e.menge}}def fuffi = new Euro(menge: 50)assert new Euro(menge: 100) == fuffi + fuffi
Der gesamte Ansatz wäre wenig nützlich, wenn Groovy nicht eine erhebliche Menge von Operatormethoden für Java Standardtypen Object, List, Map, Number, String etc. bereitstellen würde. Diese Methoden sind Teil des GDK, der Groovy-Erweiterung für die Java-Standardbibliothek. Dort sind neben den Operatormethoden auch Implementierungen von z.B. Object#println() vorhanden, so dass man println() überall im Code verwenden kann.
Das Hinzufügen von neuen Methoden zu bestehenden Klassen bewerkstelligt Groovy über sein Meta-Objekt-Protokoll. Man kann es so sehen, als ob Groovy den Punkt-Operator für die Methoden-Derefenzierung überschreiben würde.
... und so geht's weiter
In der nächsten Folge werden wir den GDK-Methoden wieder begegnen, wenn wir uns mit der speziellen Unterstützung befassen, die Groovy für die Verwendung von Number, String, List, Map und regulären Ausdrücken bietet. Neu hinzu kommt der Typ Range und das Konzept von Closures, das wesentlich zur dynamischen Natur von Groovy beiträgt.Wir werden erleben, wie sich alle Konzepte gegenseitig zu einem Gesamtbild ergänzen: die ausdrucksstarke Syntax, das reichhaltige GDK und Closures für bewegliche Logik.




