Groovy: Anhang - Glossar

Aus Wikibooks

Wenn Sie Java gelernt haben und nun mit Groovy und Grails arbeiten müssen, stoßen Sie plötzlich auf eine Reihe neuer Begriffe, die erst einmal verstanden sein wollen, bevor Sie mit diesen Werkzeugen produktiv arbeiten können. Dieses kleine Glossar soll Ihnen durch kurz gefasste Erläuterungen helfen, einen Zugang zu finden.

Binding[Bearbeiten]

Unter Binding versteht man in Groovy eine Instanz der Klasse groovy.lang.Binding, die in jedem Skript verfügbar ist und gegen die innerhalb des Skripts alle Referenzen auf undefinierte Variablen aufgelöst werden.

Durch das Binding ist es demzufolge möglich, in Groovy-Skripten Variablen zu benutzen, die nicht deklariert worden sind.

Außerdem dient das Binding dazu, Informationen zwischen einem eingebetteten Skript und dem aufrufenden Programm auszutauschen.

Innerhalb des Skripts ist das Binding unter dem Namen binding als Property verfügbar. Das folgenden Beispielskript schreibt zwei Werte in das Binding und listet dann alle im Binding enthaltenden Werte auf:

ersteVariable = 42
zweiteVariable = 'zweiundvierzig'
binding.variables.each { println it }

Builder[Bearbeiten]

Builder (deutsch auch "Erbauer") sind Klassen, deren Zweck darin besteht, Instanzen einer anderen (oder auch der eigenen) Klasse zu "bauen", d.h. sie zu erzeugen und mit bestimmten Eigenschaften zu versehen.

In Groovy beziehen Builder-Klassen ihren besonderen Reiz daraus, dass sie die dynamischen Fähigkeiten der Sprache elegant nutzen können, um auch den Aufbau komplexer hierarchischer Strukturen in übersichtlicher Weise zu gestatten.

Die Groovy-Standardbibliothek enthält zahlreiche Beispiele von Builder-Klassen, darunter den MarkupBuilder für den Aufbau von XML- und HTML-Dokumenten, den SwingBuilder für den Aufbau von Swing-Oberflächen und den NodeBuilder für den Aufbau von Hierarchien aus Node-Objekten.

Folgendes Beispielskript, das einen MarkupBuilder nutzt, generiert eine HTML-Seite mit einer sortierten Aufzählung aller aktuellen Umgebungsvariablen und deren Werten und gibt das Ergebnis auf der Konsole aus.

pageTitle = "Umgebungsvariablen"

page = new groovy.xml.MarkupBuilder()
page.html {
    head {
        title pageTitle
    }
    body {
        h1 pageTitle
        ul {
            System.getenv().entrySet().
                     sort{x,y -> x.key<=>y.key}.each {
                li "$it.key = $it.value"                    
            }
        }
    }
}

println page

Die Funktionalität dieses Beispiels entspricht dem Beispiel zu Template.

Closure[Bearbeiten]

Closures stellen eine aus der funktionalen Programmierung übernommene Neuerung dar, die zur Wirkung hat, dass sich der Programmierstil mit Groovy stark von der Java-Programmierung unterscheidet. Im Prinzip ist eine Closure nichts weiter als ein namenloses Stück Programmcode, das einer Variablen zugewiesen oder als Parameter einer Methode übergeben werden kann. In Quelltext wird die Closure durch geschweifte Klammern begrenzt.

def c = { println "Hallo Freunde" }

Man kann die Closure ausführen, in dem man ihre Methode call() aufruft...

 c.call()

oder - noch einfacher - die Closure-Variable selbst wie eine Methode behandelt:

c()

Eine Closure kann auch Parameter haben, sie sind nach der öffnenden geschweiften Klammer anzugeben und können - genau wie die Parameter einer Groovy-Methode - typisiert, untypisiert, benannt und variabel sein. Die Parameter sind vom Rumpf durch einen symbolisierten Pfeil (%%->%%) getrennt.

c = { name="Freund" -> println "Hallo $name" }
c("Klaus")

Wenn die Closure nur ein Argument hat, braucht es nicht explizit deklariert zu werden. Es hat dann den Namen it.

c = { println "Hallo $it" }

Viele vordefinierte Methoden der Groovy-Laufzeitbibliothek (sog. GDK-Methoden) haben den Zweck, Closures für Standardklassen und -interfaces nutzbar zu machen. So gibt es beispielsweise eine GDK-Methode each(), die eine als Argument übergebene Closure für jedes enthaltene Element eines Arrays, einer Collection, eines String usw. aufruft.

"abcde".each { println it }

Wenn ein Closure-Literal als letzter oder einziger Parameter eines Methodenaufrufs übergeben wird, braucht sie nicht in runde Klammern gesetzt zu werden.

Closures bilden auch ein wesentliches Element der dynamischen Programmierung in Groovy. Dies wird dadurch möglich, dass bei einer Closure eine Property delegate mit einem beliebigen Objekt übergeben werden kann, an das alle Methoden- und Property-Aufrufe weitergeleitet werden, die die Closure selbst nicht befriedigen kann.

c = { println toUpperCase() }
c.delegate="ein String"
c()  // gibt "EIN STRING" aus

DefaultGroovyMethods[Bearbeiten]

Die Klasse org.codehaus.groovy.runtime.DefaultGroovyMethods (DGM) enthält die Implementierung aller vordefinierten Instanzmethoden, um die Grails standardmäßig den Klassen und Interfaces der Java-Plattform erweitert. Diese Methoden werden auch als Groovy Development Kit (GDK) bezeichnet.

Der dabei verwendete Mechanismus ist derselbe, der auch bei Groovy-Kategorien Anwendung findet: Die vordefinierten Methoden sind als statische Methoden in GroovyDefaultMethods definiert, bei denen der Typ des ersten Parameters festlegt, auf welche Klasse bzw. welches Interface (und alle abgeleiteten bzw, implementierenden Klassen) die Methode zutrifft, und alle weiteren Parameter sind die eigentlichend Parameter der Methode. Als Beispiel sehen wir uns einmal die Methode plus() an, mit deren Hilfe das Plus-Zeichen für Strings implementiert wird:

// Java
public static String plus(String left, Object value) {
   return left + toString(value);
}

Diese Methode wird also aufgerufen, wenn Sie in einem Groovy-Programm beispielsweise 'A'.plus(1) oder 'A'+1 schreiben. Sie verhält sich genau so, als gäbe es eine Methode plus(Object) in der String-Klasse.

Da DefaultGroovyMethods wegen der Fülle der enthaltenen Methoden sehr umfangreich geworden ist, soll die Art und Weise des Vordefinierens von Methoden voraussichtlich ab Groovy 2.0 verändert werden.

Daneben gibt es eine Klasse DefaultGroovyGroovyStaticMethods für die vordefinierten statischen Methoden.

DGM[Bearbeiten]

Siehe DefaultGroovyMethods.

Duck-Typing[Bearbeiten]

Der Ausdruck Duck-Typing geht auf ein dem amerikanischen Dichter J. W. Riley zugeschriebenes Zitat zurück:

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

Im Zusammenhang mit der Programmierung ist damit gemeint, dass ein Objekt entsprechend seinen tatsächlichen Fähigkeiten behandelt wird und nicht (nur) entsprechend seiner Klassenzugehörigkeit. Diese Fähigkeit unterscheidet dynamisch typisierte Sprachen (wie Groovy und Python) von statisch typisierten Sprachen (wie Java und C++).

Beispiel: Angenommen, es gibt eine Klasse Ente, die die Methode quak() implementiert:

class Ente {
    def quak() { println 'Ente Quakt' }
}

Dann können wir versuchen, jedes Objekt zum Quaken zu bringen, auch wenn wir nicht wissen, ob es wirklich eine Ente ist:

def bringeZumQuaken (objekt) {
    try {
        objekt.quak()
    } catch (MissingMethodException x) {
        println "Keine Ente: "+objekt
    }
}

bringeZumQuaken (new Ente())   // Ente quakt
bringeZumQuaken (42)           // Keine Ente: 42

Das Metaobjekt-Protokoll ermöglicht es sogar, ein Objekt direkt danach zu befragen, ob es über eine bestimmte Methode verfügt:

if (objekt.getMetaClass().respondsTo(objekt,'quak')) ...

Duck-Typing macht die Programmierung sehr flexibel und erspart das lästige Casten in Java, bringt aber eine gewisse Unsicherheit mit sich, denn man merkt erst zur Laufzeit, ob ein Objekt eine vorausgesetzte Methode hat oder nicht.

Es kann auch Überraschungen geben, da ja auch ein Objekt einer ganz anderen Klasse die erwartet Methode implementieren kann (z.B. ein Frosch), deren Aufruf dann zu unerwarteten Ergebnissen führt...

// Nur mit Java ab 1.6
class Frosch {
    def quak() {
        new java.awt.Desktop().browse('http://www.youtube.com/watch?v=fty65g-DGSs'.toURI())
    }
}

Sieh auch Wikipedia.

Dynamisches Programmieren[Bearbeiten]

Dynamisches Programmieren (im Sinne von Groovy) ist ein Programmierstil, bei dem Objekte nicht nur so verwendet werden, wie es ihre Definition vorsieht, sondern sie zur Laufzeit mit zusätzlichen Eigenschaften und Fähigkeiten zu versehen. Dynamisches Programmieren ist nur möglich, wenn es durch die verwendete Programmiersprache unterstützt wird. Dies ist bei vielen Skriptsprachen wie Python und Ruby der Fall, und eben auch bei der Java-basierten Programmiersprache Groovy.

Dynamisches Programmieren eröffnet der Software-Entwicklung interessante neue Möglicheiten der Effizienzsteigerung. Mit ihr geht alledings auch die Typsicherheit verloren, die ein wichtiger Faktor der Stabilität von in Java programmierten Anwendungen ist. Da es beispielsweise immer geschehen kann, dass ein Objekt zur Laufzeit über Methoden verfügt, die zur Compile-Zeit nicht bekannt sind, ergeben statische Typprüfungen keinen Sinn mehr. Zur Sicherstellung der Software-Qualität müssen an ihre Stelle automatisierte dynamische Prüfungen wie Unit-Tests treten.

Wichtige Mittel der dynamischen Programmierung in Groovy sind:

  • Das Metaobjekt-Protokoll (MOP). Jeder Aufruf einer Methode oder einer Property geht nicht wie bei traditionellem Java direkt an das betreffende Objekt, sondern wird über ein anderes, als Metaklasse bezeichnetes Objekt geleitet, das diesen Aufruf in beliebiger Weise modifzieren kann.
  • Die Kategorie. Hierbei handelt es um Klassen, in denen Methoden definiert sind, die in einem definierten Anwendungsteil Objekte bestimmter Typen mit zusätzlicher Funktionalität ausstatten.
 * Das Expando. Dies ist eine Klasse, die von vorneherein dafür vorgesehen ist, dass ihre Instanzen zur Laufzeit 

Einige Elemente der Groovy-Standardbibliothek, z.B. verschiedenen Builder-Klassen, nutzen die dynamischen Möglichkeiten der Sprache intensiv aus.

Elvis-Operator[Bearbeiten]

In der Programmiersprache Groovy seit der Version 1.5 bekannter Operator, der gewissermaßen eine Kurzfassung des von Java her bekannten ternären Operators darstellt. Wird durch ein Fragezeichen gefolgt von einem Doppelpunkt (?:) symbolisiert.

Der Ausdruck a?:b liefert den Wert von a, wenn a im Sinne der ->Groovy-Wahrheit als true anzusehen ist, andernfalls den Wert von b.

assert (1 ?: 2) == 1
assert (0 ?: 2) == 2

Bisweilen wird x = a?:b als eine Kurzfassung von x = a?a:b verstanden. Dies ist jedoch nicht ganz korrekt, denn wenn a wahr ist, wird a nicht ein zweites Mal ausgewertet, sondern unmittelbar zurückgeliefert. Unter Verwendung des ternären Operators müsste man etwa Folgendes schreiben, um denselben Effekt zu erreichen:

{ def _temp = a; x = _temp ? _temp : b }

Dieser Unterschied ist wichtig, wenn a ein Ausdruck ist, der sehr Aufwändig ist, externe Ressourcen in Anspruch nimmt oder irgendwelche unerwünschten Nebeneffekte hat und daher nicht öfter als nötig ausgewertet werden sollte.

Expando[Bearbeiten]

Ein Expando ist ein Objekt, dessen Eigenschaften und Funktionalität erst zur Laufzeit festgelegt werden. Expandos sind Instanzen der Klasse groovy.util.Expando, die Properties und Methoden entweder in Form von benannten Konstruktor-Parametern oder durch nachträgliche Zuweisung erhalten.

Das folgende Beispiel eines Expando hat eine Property und eine Methode, die ihm im Konstruktor übergeben werden. Eine weitere Methode, die die erste aufruft, wird ihr nachträglich zugewiesen.

def ex = new Expando (
      wert:42,
      gibWert:{ wert }
)

ex.multi = { param -> wert * param }

Property und Methoden lassen sich genau so aufrufen wie bei einem normalen Objekt.

println ex.wert
ex.wert += 2
println ex.gibWert()
println ex.multi(10)

Expandos eignen sich besonders gut für den Einsatz als Dummy-Obekte (Mocks) in automatisierten Tests.

ExpandoMetaClass[Bearbeiten]

Die Klasse java.lang.ExpandoMetaClass (EMC) ist eine aus dem Grails-Projekt übernommene Metaklassen-Implementierung, mit deren Hilfe anderen Klassen und Interfaces oder auch einzelnen Objekten zusätzliche Methoden und Properties zugeordnet werden können.

Das folgende Beispiel zeigt, wie Sie der Klasse java.lang.Integer eine neue Methode namens quadrat() zuweisen können:

Integer.metaClass.quadrat = { delegate * delegate }
assert 10.quadrat() == 100

GPath[Bearbeiten]

Als GPath wird eine in Groovy implementierte, XPath-ähnliche Möglichkeit bezeichnet, auf Attribute, Properties und Methoden von Objekten über ganze Objektgraphen hinweg zuzugreifen.

Das folgende Beispiel erzeugt eine Liste mit den Namen aller Cousins eines Kindes über eine Objektstruktur hinweg.

def cousins = kind.eltern.eltern.kinder.kinder.@name

Siehe auch -> Elvis-Operator.

Groovlet[Bearbeiten]

Als Groovlet bezeichnet man ein Groovy-Skript, das in einem Web-Server läuft und dort ähnlich funktioniert wie ein in Java geschriebenes Servlet. Der Unterschied besteht darin, dass das Groovlet in Groovy geschrieben ist und daher an den dynamischen Möglichkeiten dieser Sprache teilhaben kann. Insbesondere lässt sich ein Groovlet jederzeit im laufenden Betrieb ändern, ohne dass ein explizites Neu-Kompilieren erforderlich ist.

Einfaches Beispiel für ein Groovlet:

println """
<html>
  <head>
    <title>Aktuelles Datum</title>
  </head>
  <body>
    Das aktuelle Datum ist: ${new Date}
  </body>
</html>
"""

Besonders effektiv lässt sich das Groovlet mit dem MarkupBuilder kombinieren.

Um ein Groovlet einsetzen zu können, wird ein standardkonformer Servlet-Container (wie Tomcat oder Jetty) benötigt, in dem das zur Groovy-Standardbibliothek gehörende Servlet groovy.servlet.GroovyServlet installiert ist.

GroovyBean[Bearbeiten]

Analog zur JavaBean ist die GroovyBean eine Softwarekomponente, auf deren Properties (d.h. Felder oder Instanzvariablen) über Methoden, die in einer bestimmten Weise definiert sind, zugegriffen werden kann (Zugriffsmethoden). Die Besonderheit bei Groovy ist, dass diese Zugriffsmethoden nicht eigens programmiert werden müssen, sondern bereits vom Compiler hinzu generiert werden (sofern es keine Implementierung diese Methoden in der Klasse selbst gibt).

So entspricht die folgende Groovy-Klasse:

// Groovy
class Mitarbeiter {
  String name
  Date geburtsdatum
}

...folgender Java-Klasse:

// Java
#import java.util.Date;

public class Mitarbeiter {
  private String name;
  private Date geburtsdatum;

  public String getName() {
    return name;
  }
  
  public void setName (String name) {
    this.name = name;
  }
  
  public Date getGeburtsdatum() {
    return geburtsdatum;
  }
  
  public void setGeburtsdatum(Date geburtsdatum) {
    this.geburtsdatum = geburtsdatum;
  }
}

... und lässt sich (auch aus Java-Programmen heraus) genau so verwenden.

Groovy Development Kit[Bearbeiten]

Groovy Development Toolkit (GDK), bisweilen auch als Groovy JDK bezeichnet, ist die etwas irreführende Bezeichnung für die Menge aller vordefinierten Methoden, mit denen die Groovy-Laufzeitumgebung zahlreiche Klassen und Interfaces des JDK erweitert.

Implementiert wird das GDK mit Hilfe einer internen Klasse namens DefaultGroovyMethods.

Groovy-Wahrheit[Bearbeiten]

Groovy hat eine andere Vorstellung davon, was wahr oder falsch ist, als Java. Während Java beispielsweise in if- und while-Anweisung zwingend einen Wert vom Typ boolean (seit Java 5.0 auch Boolean) erwartet, akzeptiert Groovy hier Werte beliebiger Typen. Als false werden dann genau folgende Werte betrachtet:

  • false und Boolean.FALSE
  • Zahlen mit dem Wert Null
  • Leere Strings
  • Kollektionen und Maps ohne Elemente
  • Iteratoren und Enumerationen, die keine weiteren Elemente liefern
  • Matcher, die keine Treffer liefern
  • null

Alle anderen Werte werden von Groovy in logischer Hinsicht wie true behandelt.

GString[Bearbeiten]

Ein GString ist eine Zeichenkette mit eingebetteten Groovy-Ausdrücken. Die Ausdrücke sind dadurch gekennzeichnet, dass sie in geschweifte Klammern eingeschlossen sind und ein Dollar-Zeichen vorangestellt ist.

def einGString = "Aktuelle Zeit: ${new Date()}"

Wenn der Ausdruck nur aus Namen und Punkten besteht, können die geschweiften Klammern auch weggelassen werden:

def ganzerName = "$person.vorname $person.name"

GStrings ersparen in der Programmierung komplizierte String-Additionen und bieten einige zusätzliche Möglichkeiten. Zu beachten ist, dass die eingebetteten Ausdrücke zwar bei der Definition des GString ausgewertet werden, aber nicht immutabel sind. So gibt das folgende Skript beispielsweise zwei unterschiedliche Zeitangaben aus:

d = new Date()
datumGString = "Aktuelle Zeit: $d"
println datumGString
d.hours++
println datumGString

Das Einbetten von Ausdrücken in Strings kann man verhindern, indem man einen String in einfache Hochkommas einschließt oder indem man dem Dollarzeichen ein Backslash voranstellt (\${...}).

JSR-241[Bearbeiten]

TODO

Kategorie[Bearbeiten]

Eine Kategorie ist eine Klasse, die dazu dient, andere Klassen dynamisch mit zusätzlichen Methoden zu versehen. In der Kategorienklasse müssen dazu statische Methoden definiert werden, deren erstes Argument jeweils vom Typ (Klasse oder Interface) ist, auf den die Methode angewendet werden soll. An Objekten dieses Typs ist die Methode dann unter demselben Namen und mit den übrigen Parametern verfügbar). Mit Hilfe der für alle Klassen in Groovy vordefinierten Methoden use() können Sie die Kategorienklasse dann aktivieren; sie gilt innerhalb der als Argument anzugebenden Closure.

Folgendes Beispiel-Skript einer Kategorienklasse definiert eine Methode isPrime() für Integer-Werte, die ermittelt, ob das aktuelle Objekt eine Primzahl ist.

class PrimeCategory {

    static boolean isPrime(Integer self) {
        if (self < 2)  return false
        if (self == 2) return true
        if (self % 2 == 0) return false
        for (int i=3g; i*i<=self; i+=2) {
            if (self%i==0) return false
        }
        true
    }
}

use (PrimeCategory) {
    println 5.isPrime()  // true
    println 27.isPrime() // false
}

Das zweite Beispiel definiert eine Methode removeAll() für Strings, die alle Vorkommen eines regulären Ausdrucks, der als Argument anzugeben ist, aus dem aktuellen Objekt entfernt.

class ExtStringCategory {
    static String removeAll (String self, String s) {
        self.replaceAll(s,)
    }
}

use (ExtStringCategory) {
   println "Hallihallo".removeAll('[lio]')
}

Metaklasse[Bearbeiten]

TODO

Metaobjekt-Protokoll[Bearbeiten]

TODO

Skript[Bearbeiten]

TODO

Template[Bearbeiten]

Ein Template ist ein Text, der in bestimmter Weise gekennzeichnete Platzhalter enthält, die mit Hilfe einer Template-Engine durch Daten ersetzt werden können. Ein beliebtes Einsatzgebiet für Templates ist die Generierung von Webseiten.

Die Groovy-Laufzeitbibliothek enthält ein komplettes Framework für die Verarbeitung von Templates und mehrere vorgefertigte Template-Engines. Darüber hinaus gibt es eine Klasse TemplateServlet, die direkt dazu eingesetzt werden kann, in einem Servlet-Container Webseiten aus Template-Texten zu generieren.

Eine der Template-Engines, die SimpleTemplateEngine, funktioniert ähnlich wie JavaServer Pages, d.h sie interpretiert alles, was zwischen den Zeichenfolgen <% und %> steht, als Groovy-Code, und außerdem kann man Groovy-Ausdrücke in gleicher Weise wie die Platzhalter in GStrings eingefügen.

Das folgendee Beispielskript generiert mit Hilfe eines Templates eine HTML-Seite, die eine sortierte Aufzählung aller aktuellen Umgebungsvariablen und deren Werte enthält, und gibt das Ergebnis auf der Konsole aus.

text = '''
<% pageTitle = "Umgebungsvariablen" %>
<html>
    <head>
        <title>$pageTitle</title>
    </head>
    <body>
        <h1>$pageTitle</h1>
        <ul>
<% System.getenv().entrySet().sort{x,y -> x.key<=>y.key}.each { %>
            <li>$it.key = $it.value</li>
<% } %> 
        </ul>
    </body>
</html>
'''

engine = new groovy.text.SimpleTemplateEngine()
template = engine.createTemplate(text)
println template.make()

Die Funktionalität dieses Beispiels entspricht dem Beispiel zu Builder.

In Grails gibt es eine ähnliche, aber erheblich erweiterte Technologie namens Groovy Server Pages (GSP). Der Begriff Template hat in Grails eine andere Bedeutung.

Vordefinierte Methode[Bearbeiten]

TODO