Kurz: Closures mit delegate ermöglichen eigene Fachsprachen, wie man sie aus Gradle kennt:
Teil des Kurses Groovy
Builder und DSLs
Closures mit delegate ermöglichen eigene Fachsprachen, wie man sie aus Gradle kennt:
class HtmlBuilder {
StringBuilder sb = new StringBuilder()
int tiefe = 0
def methodMissing(String name, args) {
def attribute = args.find { it instanceof Map } ?: [:]
def inhalt = args.find { it instanceof Closure }
def text = args.find { it instanceof String }
def attr = attribute.collect { k, v -> " $k=\"$v\"" }.join()
sb << " " * tiefe << "<$name$attr>"
if (inhalt) {
sb << "\n"
tiefe++
inhalt.delegate = this
inhalt()
tiefe--
sb << " " * tiefe
} else if (text) sb << text
sb << "</$name>\n"
}
}
def b = new HtmlBuilder()
b.html {
body(class: "start") {
h1("Hallo")
p("Absatz")
}
}
print b.sbAusgabe:
<html>
<body class="start">
<h1>Hallo</h1>
<p>Absatz</p>
</body>
</html>Konfigurations-DSL mit delegate
class Konfig {
String name
List<String> pakete = []
Map<String, String> umgebung = [:]
void paket(String p) { pakete << p }
void umgebung(String k, String v) { umgebung[k] = v }
}
def konfiguriere(Closure c) {
def k = new Konfig()
c.delegate = k
c.resolveStrategy = Closure.DELEGATE_FIRST
c()
k
}
def k = konfiguriere {
name = "Mein Projekt"
paket "groovy"
paket "spock"
umgebung "MODE", "test"
}
println k.name
println k.pakete
println k.umgebungAusgabe:
Mein Projekt
[groovy, spock]
[MODE:test]Metaprogrammierung
Mit metaClass ändert man Klassen zur Laufzeit – auch Java-Klassen:
String.metaClass.istPalindrom = { -> delegate == delegate.reverse() }
Integer.metaClass.verdopple = { -> delegate * 2 }
println "anna".istPalindrom()
println "hallo".istPalindrom()
println 21.verdopple()
class Dyn {
def methodMissing(String name, args) { "dynamisch: $name(${args.join(', ')})" }
def propertyMissing(String name) { "Property $name" }
}
def d = new Dyn()
println d.irgendwas(1, 2)
println d.foo
class Kategorie {
static String exklamation(String s) { s + "!" }
}
use(Kategorie) { println "Hallo".exklamation() }
def obj = new Expando()
obj.name = "Mia"
obj.gruss = { "Hallo, ${delegate.name}" }
println obj.gruss()
println "text".metaClass.respondsTo("text", "size").size() > 0
println Integer.class.methods.size() > 10Ausgabe:
true
false
42
dynamisch: irgendwas(1, 2)
Property foo
Hallo!
Hallo, Mia
true
trueStatisch typisieren
@CompileStatic prüft Typen zur Übersetzungszeit und macht Groovy so schnell wie Java:
import groovy.transform.CompileStatic
@CompileStatic
int summiere(List<Integer> zahlen) {
int s = 0
for (int z : zahlen) s += z
return s
}
@CompileStatic
String beschreibe(Object o) {
switch (o) {
case Integer: return "Zahl ${o}"
case String: return "Text ${o}"
default: return "unbekannt"
}
}
println summiere([1, 2, 3, 4])
println beschreibe(5)
println beschreibe("x")
println beschreibe(2.5)Ausgabe:
10
Zahl 5
Text x
unbekanntMerke
delegateund Closures erlauben DSLs wie Gradle oder JenkinsmethodMissing,propertyMissingundmetaClasserweitern Klassen zur Laufzeit- Categories (
use) undExpandosind leichtgewichtige Alternativen @CompileStaticbringt Typprüfung und Tempo
Übungsaufgabe
Baue eine kleine DSL für Einkaufslisten: einkauf { artikel "Milch", 2 }.
Quiz zur Selbstkontrolle
Wofür dient delegate bei Closures?
- Legt fest, an welches Objekt Methodenaufrufe gehen (richtig)
- Löscht Variablen
- Startet Threads
- Schützt Daten
Was macht methodMissing?
- Wird aufgerufen, wenn eine Methode nicht existiert (richtig)
- Wirft immer einen Fehler
- Löscht Methoden
- Prüft Typen
Was bewirkt @CompileStatic?
- Statische Typprüfung und schnelleren Code (richtig)
- Dynamisches Typisieren
- Cache aktivieren
- Tests ausführen
Weiter im Kurs
Zurück: Strings, Dateien und Datenformate
Weiter: Nebenläufigkeit und Java-Integration
Alle Kapitel: Groovy im Überblick