Während man genügend Erklärungen zu Default-Methoden in Java 8 im Netz findet, scheinen Erklärungen, was man damit in Bezug auf die Software-Architektur besser machen kann, ziemlich rar zu sein. Ich habe dazu eigentlich nur das gefunden:
Template-Method-Pattern: http://java.dzone.com/articles/template-method-pattern-using
Eigentlich ist nicht besonders verwunderlich, dass für viele das nachträgliche Hinzufügen von Methoden zu Interfaces (etwa foreach()
zu Iterator
in Java 8) als Hauptanwendungsgebiet angesehen wird. Schließlich **hießen **Default-Methoden einmal so, nämlich Erweiterungsmethoden (Extension Methods).
Wer Scalas Traits (als eine Form der Mixin-Vererbung) kennt, weiß auch, dass man damit noch mehr anstellen kann, nur ist Scala eine andere Sprache, und nicht alles dort lässt sich eins zu eins nach Java übersetzen.
Deshalb will ich einmal an dieser Stelle einmal über die sinnvolle Anwendungen von Default-Methoden brainstormen. Spontan kommen mir neben den bereits genannten noch vier Anwendungsfälle in den Sinn:
-
Vermeidung abstrakter Adapter: Oft hat man in einer Bibliothek (und auch in den Java-APIs) ein Interface Foo, und darauf aufbauend eine abstrakte Klasse zur bequemen Implementierung (oft FooAdapter, AbstractFoo oder DefaultFoo genannt). Typische Beispiele sind
java.awt.event.MouseAdapter
oderjava.util.AbstractList
. Aber diesen Job kann in Zukunft das Interface selbst übernehmen, wenn eine zustandslose Implementierung möglich ist. -
Stateless Diamond: Das bekannte Diamant-Problem tritt bei Mixin-Vererbung nicht auf. Manchmal benötigt man diese z.B. bei mathematischen Strukturen (etwa bei Gruppe und Magma, siehe http://en.wikipedia.org/wiki/Magma_(algebra) )
-
Verallgemeinerndes Sub-Interface: Angenommen wir hätten ein Interface
Equals<T>
mit einer “symmetrischen” Vergleichsmethodeboolean eq(T t1, T t2)
. Dann wäre es sinnvoll,Comparator<T>
als dessen Sub-Interface anzusehen. Wenn man aber sowieso die üblicheint compare(T t1, T t2)
-Methode implementieren muss - die in einem gewissen Sinneequals
“verallgemeinert”, kann man mit diesem Wissen auch davon eine Standard-equals
-Implementierung ableiten, nämlichreturn compare(t1, t2) == 0;
. -
Decorator-Pattern: Hat man z.B. in einem GUI-Framework eine Hierarchie ausgehend von einer abstrakten
Component
-Klasse, die etwa einedrawBorder()
und einedrawBackground()
-Methode besitzt, könnte man Interfaces schreiben, die dafür jeweils Default-Methoden bereitstellen, und dann “on the fly” Komponenten zusammensetzen:class MyBorderLabel extends Label implements LineBorder, TransparentBackground
. Ich weiß nicht so richtig, ob diese Technik wirklich praktisch ist, insbesondere weil die entsprechenden Methoden in der normalen Vererbungshierarchie nicht implementiert sein dürfen. Ebenfalls hinderlich ist, dass man (im Gegensatz zu Scala) zusätzliche Interfaces nicht einfach bei der Instantiierung “mitgeben kann”, also etwas wieLabel myLabel = new Label("foo") implements LineBorder, TransparentBackground;
nicht erlaubt ist.
Fällt euch noch was ein?