Änderungen an Schnittstellen: Code-Kompatibilität und Binär-Kompatibilität

Sind Schnittstellen einmal deklariert und in einer großen Anwendung verbreitet, so sind Änderungen nur schwer möglich, da sie schnell die Kompatibilität brechen. Wird der Name einer Parametervariablen umbenannt, ist das kein Problem, aber bekommt eine Schnittstelle eine neue Operation, führt das zu einem Übersetzungsfehler, wenn nicht automatisch alle implementierenden Klassen diese neue Methode implementieren. Framework-Entwickler müssen also sehr drauf achten, wie sie Schnittstellen modifizieren, doch sie haben es in der Hand, wie weit die Kompatibilität gebrochen wird.

Geschichtsstunde

Schnittstellen später zu ändern, wenn schon viele Klassen die Schnittstelle implementieren, ist eine schlechte Idee. Denn erneuert sich die Schnittstelle, etwa wenn nur eine Operation hinzukommt oder sich ein Parametertyp ändert, dann sind plötzlich alle implementierenden Klassen kaputt. Sun selbst hat dies bei der Schnittstelle java.sql.Connection riskiert. Beim Übergang von Java 5 auf Java 6 wurde die Schnittstelle erweitert, und keine Treiberimplementierungen konnten mehr compiliert werden.

Code-Kompatibilität und Binär-Kompatibilität

Es gibt Änderungen, die führen zwar zu Compilerfehlern, wie neu eingeführten Operationen, sind aber zur Laufzeit in Ordnung. Bekommt eine Schnittstelle eine neue Methode, so ist das für die JVM überhaupt kein Problem. Die Laufzeitumgebung arbeitet auf den Klassendateien selbst und sie interessiert es nicht, ob eine Klasse brav alle Methoden der Schnittstelle implementiert; sie löst nur Methodenverweise auf. Wenn eine Schnittstelle plötzlich „mehr“ vorschreibt, hat sie damit kein Problem.

Während also fast alle Änderungen an Schnittstellten zum Bruch der Codebasis führen, sind doch einige Änderungen für die JVM in Ordnung. Wir nennen das Binär-Kompatibilität. Zu den binär-kompatiblen Änderungen zählen:

  • Neue Methode hinzufügen
  • Schnittstelle erbt von einer zusätzlichen Schnittstelle
  • Hinzufügen/Löschen einer throws-Ausnahme
  • Letzten Parametertyp von T[] in T… ändern
  • Neue Konstanten, also statische Variablen hinzufügen
  • Die Anzahl der binär-inkompatiblen Änderungen sind jedoch gradierender. Verboten sind:

  • Ändern des Methodennamens
  • Ändern der Parametertypen und Umsortieren der Parameter
  • Formalen Parameter hinzunehmen oder entfernen
  • Strategien zum Ändern von Schnittstellen

    Falls die Schnittstelle nicht groß veröffentlicht wurde, so lassen sich einfacher Änderungen vornehmen. Ist der Name einer Operation zum Beispiel schlecht gewählt, wird ein Refactoring in der IDE den Namen in der Schnittstelle genauso ändern wie auch alle Bezeichner in den implementierenden Klassen. Problematischer ist es, wenn externe Nutzer sich auf die Schnittstelle verlassen. Eine Lösung ist, diese Klienten ebenfalls zur Änderung zu zwingen, oder auf „Schönheitsänderungen“, wie dem Ändern des Methodenamens, einfach zu zu verzichten.

    Kommen Operationen hinzu, hat sich eine Konvention etabliert, die im Java-Universum oft anzutreffen ist: Soll eine Schnittstelle um Operationen erweitert werden, so gibt es eine neue Schnittstelle, die die alte erweitert, und auf „2“ endet; java.awt.LayoutManager2 ist so ein Beispiel aus dem Bereich der grafischen Oberflächen, Attributes2, EntityResolver2, Locator2 für XML-Verarbeitung sind weitere. Ein Blick auf die API vom Eclipse-Framework (http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/index.html?overview-summary.html) zeigt, dass bei mehr als 3500 Typen dieses Muster um die 70 Mal angewendet wurde.

    Seit Java 8 gibt es eine weitere Möglichkeit Operationen in Schnittstellen hinzuzufügen, sogenannte Virtuelle Erweiterungsmethoden. Sie erweitern die Schnittstelle, fügen aber gleich schon eine vorgefertigte Implementierung mit, sodass Unterklassen nicht zwingend eine Implementierung anbieten müssen.

    Ähnliche Beiträge

    Veröffentlicht in Insel

    Schreibe einen Kommentar

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert