In diesem Beitrag möchte ich dir die objektorientierte Programmierung mit Apache Groovy näher bringen.
Bisher haben wir unsere Groovy Skripte lediglich in einer Datei geschrieben, welche einfach ausgeführt wurde. Das ist für kleine Skripte völlig okay, wenn wir jedoch große Business-Anwendungen entwickeln wollen, dann sollten wir eine gewisse Struktur einbauen und unseren Code aufteilen.
Die offizielle Dokumentation zur objektorientierten Programmierung in Apache Groovy findest du unter https://groovy-lang.org/objectorientation.html.
Erzeugen einer Klasse
Eine Groovy Klasse ist der Java Klasse sehr ähnlich und auch voll kompatibel mit Java. D.h. du kannst eine Java Klasse aus Groovy importieren und ableiten.
Eine Groovy Klasse erzeugen wir mit dem Schlüsselwort „class“ jedoch entfällt der aus Java ggf. bekannte Accessmodifier „public“, da in Groovy generell alle Klassen „public“ sind.
Innerhalb dieser Klasse können wir nun Felder und Funktionen definieren, welche die Klasse auszeichnen.
class Person { String firstname String lastname def greeting() { "Hello from $firstname $lastname" } }
Erzeugen wir uns nun ein erstes Objekt:
println new Person(firstname:"Stefan", lastname:"Draeger").greeting()
Die Ausgabe in der Konsole ist wie folgt:
Hello from Stefan Draeger
Was einem auffällt, ist der Konstruktor, welchen wir für die Erzeugung unserer Klasse verwenden, hier nutzen wir die Feldnamen als Parameter und können diesen, so die Werte übergeben.
Natürlich könnten wir auch zuerst nur eine leere Hülle erzeugen und die Werte per Setter setzen.
Person p1 = new Person() p1.setFirstname("Max") p1.setLastname("Mustermann") println p1.greeting()
Was einem erfahrenen Javaentwickler auffallen wird ist, dass wir Funktionen verwenden, welche in der Klasse nicht definiert sind. In Groovy werden für alle öffentliche Felder getter & setter automatisch bereitgestellt (dieses können wir durch eine Annotation steuern).
private Felder in Klassen
Wenn wir jedoch ein Feld mit dem Accessmodifier „private“ markieren, dann haben wir keinen Zugriff mehr über einen Setter auf diesen, sondern nur noch über den Konstruktor. Wir müssten also den Setter (und Getter) selber schreiben.
Im nachfolgenden Beispiel habe ich das Feld „lastname“ auf „private“ gesetzt und erhalte beim Ausführen des Skriptes dann eine Exception das, die Funktion „setLastname()“ nicht gefunden wurde.
class Person { String firstname private String lastname def greeting() { "Hello from $firstname $lastname" } }
Person p1 = new Person() p1.setFirstname("Max") p1.setLastname("Mustermann") println p1.greeting()
Caught: groovy.lang.MissingMethodException: No signature of method: person.Person.setLastname() is applicable for argument types: (String) values: [Mustermann]
Vererbung
Mit Vererbung können wir öffentliche Attribute (Felder, Funktionen) von einer anderen Klasse erben und wiederverwenden.
Nehmen wir zunächst ein einfaches Beispiel, wir wollen ein Fahrzeugpool pflegen, dort haben wir diverse Kraftfahrzeuge (LKW, Auto, Motorrad) enthalten. Allen diesen Kraftfahrzeugen ist gleich, dass diese folgende Attribute haben:
- Anzahl X Räder,
- Anzahl X Sitzplätze,
- Farbe,
- PS,
- Gewicht,
- Zulässiges Gesamtgewicht
Jetzt könnte man einfach für jeden der genannten Typen Kraftfahrzeuge eine Klasse mit den oben genannten Attributen erstellen, das würde funktionieren, hätte jedoch nichts mit objektorientierte Programmierung zu tun.
import groovy.transform.ToString @ToString class Kraftfahrzeug { int anzahlRaeder; int anzahlSitzplaetze; String farbe; int ps; double gewicht; double zulGesamtgewicht; }
Wir können nun beliebig viele Instanzen von dieser Klasse erzeugen und so unseren Fahrzeugpool aufbauen.
Kraftfahrzeug lkw = new Kraftfahrzeug() lkw.setAnzahlRaeder(8) lkw.setAnzahlSitzplaetze(3) lkw.setFarbe("blau") lkw.setPs(800) lkw.setGewicht(1.7) lkw.setZulGesamtgewicht(40) Kraftfahrzeug auto = new Kraftfahrzeug() auto.setAnzahlRaeder(4) auto.setAnzahlSitzplaetze(5) auto.setFarbe("rot") auto.setPs(112) auto.setGewicht(1.5) auto.setZulGesamtgewicht(3.5) Kraftfahrzeug motorad = new Kraftfahrzeug() auto.setAnzahlRaeder(2) auto.setAnzahlSitzplaetze(2) auto.setFarbe("schwarz") auto.setPs(130) auto.setGewicht(250) auto.setZulGesamtgewicht(1) println lkw println auto println motorad
Ausgabe in der Konsole:
Kraftfahrzeug(8, 3, blau, 800, 1.7, 40.0)
Kraftfahrzeug(2, 2, schwarz, 130, 250.0, 1.0)
Kraftfahrzeug(0, 0, null, 0, 0.0, 0.0)
Wir haben aber das Problem, dass wir diese Objekte nicht unterscheiden können, ob es ein Lkw, Auto oder Motorrad ist. Hier müssten wir also entweder ein zusätzliches Feld pflegen oder wir nutzen die objektorientierte Programmierung und erben von der Klasse „Kraftfahrzeug“.
Fangen wir mit der Klasse „Auto“ an, hier erben wir nur von der Klasse „Kraftfahrzeug“ und fügen zunächst keine weiteren Felder oder Funktionen hinzu.
import groovy.transform.ToString @ToString class Auto extends Kraftfahrzeug{ }
Da ich an der Klasse Kraftfahrzeug die Annotation @ToString gesetzt habe, muss ich diese auch an die neue Klasse Auto setzen, da sonst die toString Methode der Klasse Kraftfahrzeuge aufgerufen wird und dieses zu Verwirrung führen kann.
Wenn wir nun ein Objekt vom Typ Auto erzeugen, dann wird in der Konsole die Ausgabe erzeugt, dass wir zwei unterschiedliche Objekte haben.
Kraftfahrzeug lkw = new Kraftfahrzeug() lkw.setAnzahlRaeder(8) lkw.setAnzahlSitzplaetze(3) lkw.setFarbe("blau") lkw.setPs(800) lkw.setGewicht(1.7) lkw.setZulGesamtgewicht(40) Auto auto = new Auto(); auto.setAnzahlRaeder(4) auto.setAnzahlSitzplaetze(5) auto.setFarbe("rot") auto.setPs(112) auto.setGewicht(1.3) auto.setZulGesamtgewicht(3.5) println lkw println auto
de.draegerit.firstprj.oop.Kraftfahrzeug(8, 3, blau, 800, 1.7, 40.0)
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)
Nun können wir uns eine Liste mit den Kraftfahrzeugen aufbauen und verwalten.
List<Kraftfahrzeug> kfzPool = new ArrayList<>() Kraftfahrzeug lkw = new Kraftfahrzeug() lkw.setAnzahlRaeder(8) lkw.setAnzahlSitzplaetze(3) lkw.setFarbe("blau") lkw.setPs(800) lkw.setGewicht(1.7) lkw.setZulGesamtgewicht(40) Auto auto = new Auto() auto.setAnzahlRaeder(4) auto.setAnzahlSitzplaetze(5) auto.setFarbe("rot") auto.setPs(112) auto.setGewicht(1.3) auto.setZulGesamtgewicht(3.5) for(int i=0;i<5;i++) { kfzPool.add(lkw) kfzPool.add(auto) } println kfzPool
Wir haben nun eine Liste mit 6 Kraftfahrzeugen erzeugt und diese können mir nun mit dem Schlüsselwort „instanceof“ auf den Typ prüfen.
kfzPool.each { kfz -> if(kfz instanceof Auto) println kfz }
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)
de.draegerit.firstprj.oop.Auto(5, 112, rot, 3.5, 1.3, 4)