Mixins mit Java

By Gerald Mücke | September 18, 2015

Mixins mit Java

Unter Mixins versteht man zusätzliche Funktionalität, die einer Klasse hinzugefügt werden kann. Sie sind eine spezielle Form der Mehrfachvererbung, bei der Eigenschaften oder Verhalten von mehreren „Eltern“ geerbt werden. Mit Mixins lassen sich zwei Problemgebiete lösen: Man möchte viele Optionale Features für eine Klasse anbieten Man möchte ein bestimmtes Feature für viele verschiedene Klassen anbieten Mit Hilfe von Mixins lassen sich Domain Modelle einfach halten, indem nur die essentiellen Eigenschaften definiert werden und optionale Eigenschaften über Mixins hinzugefügt werden können. Auch lassen sich externe Bibliotheken auf diese Weise erweitern ohne auf dedizierte Extension Points angewiesen zu sein. Weiterhin können Querschnittsfunktionen über Mixins realisiert werden, so dass diese nicht Teil der Typhierarchie und damit des Domänen-Modells sein müssen. Sie stelllen damit eine Alternative zu Aspektorientierter Programming dar. In Java werden seit Version 8 Mixins in Form von default Methoden auf Interfaces unterstützt. Damit können einer Klasse durch Implementierung eines Interfaces mit default Methoden Funktionen hinzugefügt werden.

public interface MyMixin {
  default String helloWorld() {
    return "Hello World";
  }
}
 
public class MixinClass extends OtherClass implements MyMixin{
}
 
System.out.println(new MixinClass().helloWorld());

Als Beispiel Case sei hier eine Swing Applikation genannt. Anders als JavaFX sind die Swing Klassen und Methoden nicht für Funktionale Programmierung ausgelegt. Inbesondere Event Handler müssen noch mühsam implementiert und können nicht über Lambda-Ausdrücke definiert werden. Mit Hilfe von Mixins lässt sich hier die Möglichkeit schaffen, Funktionen des Event Handlers an eine im Mixin Interface definierte Methode als Lambda-Ausdruck, d.h. als FunctionalInterface, zu übergeben. Das Mixin selbst definiert eine Methode, die vom zu erweiternden Objekt bereits implementiert ist. Die default Method nimmt die Instanz des FunctionalInterface entgegen und wrappt es in einen EventHandler, der an die im interface definierte und bereits implementierte Methode übergeben wird.

public interface FocusEventProducerMixin {
  void addFocusListener(FocusListener l); //Methode des Objekts
 
  default void addFocusGainedListener(Consumer<FocusEvent> c) {
    addFocusListener(new FocusAdapter(){
      public void focusGained(FocusEvent e) {
        c.accept(e);
      }
    });
  }
}

Die Swing Klasse (oder auch jede andere 3rd-Party Klasse) wird nun um einen Subtyp erweitert, der das Mixin Interface implementiert.

public class JButtonLambda
  extends JButton
  implements FocusEventProducerMixin{
}

Zu guter letzt wird die neue Klasse instanziiert und die EventHandler Funktion als Lambda Ausdruck übergeben:

JButtonLambda jbutton = ...
jbutton.addFocusGainedListener(e -> System.out.println(e));
comments powered by Disqus