In diesem Beitrag stelle ich dir vor, wie du Interfaces in Groovy implementieren und nutzen kannst.
Im ersten Teil zur objektorientierten Programmierung mit Groovy habe ich dir bereits gezeigt, wie du Klassen und abstrakte Klassen erstellen und somit mit Vererbung arbeiten kannst. Hier soll es nun um die Interfaces gehen.
In diesem Beitrag verwende ich die Eclipse IDE welche ich für die Entwicklung von Groovy Skripte erweitert habe, wie du dieses machen kannst, habe ich im Beitrag Einrichten von Eclipse zum Entwickeln in Apache Groovy ausführlich erläutert.
Was ist ein Interface?
Ein Interface definiert eine Art Muster, welchem eine Klasse entsprechen muss, die dieses Interface implementiert. D.h. es werden die Methoden definiert, aber nicht ausprogrammiert. Die eigentliche Bussiness-Logik wird in den Klassen oder Serviceimplementationen geschrieben.
Auf der Seite https://docs.groovy-lang.org/docs/next/html/documentation/#interfaces findest du die offizielle Dokumentation zu den Interfaces in Apache Groovy.
Wie wird ein Interface aufgebaut?
Ein Interface wird eingeleitet mit dem Schlüsselwort “interface” gefolgt von einem Namen.
interface ReportService
Ein Interface kann weitere Interfaces erweitern, dafür werden diese mit dem Schlüsselwort “extends” Kommasepariert aufgeführt.
interface ReportService<T> extends Serializable, SQLData
ein kleines Beispiel
Starten wir mit einem kleinen Beispiel, zunächst wollen wir einen Service schreiben, welcher nur eine Funktion bietet, der Ausgabe einer Zeile auf der Kommandozeile.
interface OutputService { def void printlnWithTimestamp(String text) }
Die Implementation des Services kann dann wiefolgt aussehen:
import java.text.SimpleDateFormat class OutputServiceImpl implements OutputService{ @Override public void printlnWithTimestamp(String text) { SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:SS") String datetime = df.format(new Date()) println("["+datetime+"] "+text) } }
Wenn wir dieses nun wie folgt initialisieren und aufrufen:
OutputService outputService outputService = new OutputServiceImpl() outputService.printlnWithTimestamp("Hallo Welt!")
Dann erhalten wir folgende Ausgabe auf der Kommandozeile / im Terminal der IDE:
Erweitern des Groovy Interfaces um eine Methode
Wenn wir nun das Interface um eine weitere Methode erweitern, dann werden im gleichen Zuge gezwungen diese auch in der Implementierung zu programmieren. Dieses ist ein Vorteil bei Interfaces und auch abstrakten Klassen, denn so zwingt man andere Entwickler sich an das Format zu halten.
Eine weitere Implementierung
Natürlich kann man auch eine weitere Implementierung zu diesem Interface schreiben und aufrufen und so zwischen mehreren Funktionen mit dem gleichen Namen wechseln.
In meinem Fall erstelle ich mir eine weitere Implementation mit dem Zusatz das, dass Datumsformat auf Deutsch und zusätzlich der Benutzername ausgegeben wird.
import java.text.SimpleDateFormat class OutputServiceImpl2 implements OutputService{ @Override public void printlnWithTimestamp(String text) { SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:SS") String datetime = df.format(new Date()) String username = System.getProperty("user.name"); println("["+datetime+", "+username+"] "+text) } }
Wenn wir dieses nun initialisieren und ausführen, so erhalten wir nachfolgende Ausgabe:
Erkennbar ist, dass wir in der Zeile 2 unser Interface definieren und jeweils in den Zeilen 4 & 7 eine andere Implementation zuweisen. Dieses funktioniert, da wir für unsere Klassen eine Vorlage nutzen und somit mit dieser arbeiten.
Groovy-Interface-Magic
Kommen wir nun zur Groovy Magic mit Interfaces. Wenn wir eine “normale” Klasse mit den Methoden aus dem Interface implementieren, ohne das Interface selber zu implementieren, dann können wir eine Instanz von dieser Klasse in das Interface umwandeln.
import java.text.SimpleDateFormat public class OutputClass { public void printlnWithTimestamp(String text) { SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:SS") String datetime = df.format(new Date()) println("["+datetime+"] "+text) } public void eineWeitereMethode() { println("Hier passiert nichts!") } }
Um nun diese Klasse in eine Instanz unseres Interfaces zu verwandeln, benötigen wir den coercion operator, mit diesem können wir diverse Datentypen umwandeln (ähnlich wie ein casting).
OutputClass outputClass = new OutputClass() service = outputClass as OutputService assert service instanceof OutputService
Wenn wir dieses nun aufrufen, dann wird keine Fehlermeldung ausgegeben und unsere Klasse verhält sich wie das Interface. Jedoch verlieren wir die Funktion “eineWeitereMethode”, denn diese ist im Interface nicht definiert.