16.1.3 Ein Interface implementieren

 

  Dieser kleine Abschnitt ist hier nur der Vollständigkeit halber eingefügt und kann im Moment übersprungen werden. Es handelt von Interfaces, eine Art dritte Variante von Klassen.

Ein Interface ein Klasse, die nur abstrakte Methoden besitzt. Selbstredend, dass man auch von ihnen keine Instanzen bilden kann, es sei denn, man implementiert dabei gleichzeitig die abstrakten Methoden1). Statt zu sagen, eine Klasse A erbt vom Interface B, sagen wir lieber A implementiert das Interface B und schreiben anstelle des reservierten Wortes extends dann implements.

 

Quellcode für ein Beispiel
public interface A{

  public abstract void a();

}

public class B implements A{

  public void a(){

    //....

  }

}
UML


Abb. 16.1.3.1 B implementiert das
Interface A

 

Anwendung
public class InterfaceTest1 {



  public static void main (String[] args) {

      B b = new B();

      b.a();

  }

}

aber auch

public class InterfaceTest2 {



  public static void main (String[] args) {

     A a = new A() {

       public void a(){

         //...

       }

     };

     a.a();

  }

}

  Das zweite Beispiel zeigt, wie man das Verbot, von einer abstrakten Klasse oder Interface Instanzen zu bilden, umgehen kann. Man stellt sicher, dass beim Erzeugen der Instanz die geerbte, abstrakte Methode gleich mit implementiert wird. 
 
Mehrfach-
vererbung
Wozu setzt man Interfaces ein?

1. Das Problem der Mehrfachvererbung
Mehrfachvererbung mit extends gibt es nicht. Denn es könnte Konflikte geben. Würde z.B. eine Klasse X von den Klassen A und B erben und in den beiden Klassen A und B gäbe es jeweils eine Methode, die sich zwar in der Funktionalität aber nicht in Namen und Signatur unterscheiden. Ein Objekt der Klasse X ruft nun diese Methode auf. Welche Methode gilt nun, die die in A, oder die die in B implementiert ist?  Um diese Konflikte zu vermeiden sind in Java Mehrfachvererbungen verboten. Dagegen kann X mehrere Interfaces implementieren, da die geerbten Methoden alle abstrakt sind, und es somit zu keinen Konflikten kommen kann.

2. Zusicherung und Konsistenz
Der Sinn von Interfaces liegt darin, dass man den Programmierer zwingt, bestimmte Methoden mit vorgefertigter Signatur zu implementieren, wie etwa die
run()-Methode des Interfaces in Runnable. So kann man, nur wenn run() der Signatur nach richtig implementiert ist, die 'Runnable-Instanz' sinnvoll an einen Konstruktor von Thread übergeben. Man kann also einem Konstruktor von Thread keine beliebe Klasse übergeben, selbst wenn man dort run() vernünftig implementiert hat. Die Zusicherung fehlt eben. Dieser Sachverhalt wird noch klarer, wenn wir uns eingehender mit Threads-Programmierung beschäftigen.

Es gibt auchInterfaces, die mehrerer (abstrakte) Methoden besitzen, wie etwa das Interface MouseMotionListener. Es hat die abstrakten Methoden mouseDragged(MouseEvent e) und MouseMoved(MouseEvent e). Implementiert man nun das Interface MouseMotionListener, so muss man beide abstrakte Methoden implementieren, auch wenn man nur eine der beiden benötigt. Das ist umständlich. Java stellt für MouseMotionListener wie auch für alle Interfaces mit mehr als einer abstrakten Methode sog. Adapterklassen zur Verfügung. So gibt es die Klasse MouseMotionAdapter, die die abstrakten Methoden von MouseMotionListener implementiert. Allerdings haben die so implementierten Methoden keinerlei Funktionalität. Das kann man so sehen: Im Inteface MouseMotionListerner gibt es, wie schon erwähnt, die Methode

public abstract void mousedragged(MouseEvent e);

in der Adapterklasse wird daraus:

public void mousedragged(MouseEvent e){}

Die Methode ist jetzt nicht mehr abstrakt, im Anweisungsblock steht aber nichts, so dass die implementierte Methode keinerlei Funktionalität besitzt: sie kann nichts. Das gleiche macht man nun mit allen anderen abstrakten Klassen des Interfaces. Statt nun das Interfaces MouseMotionListener(MouseEvent e) zu implementieren (implements) und dabei alle geerbten abstrakten Methode zu überschreiben, kann man von MousMotionAdapter erben (extends) und muss nur noch die gewünschte Methode überschreiben. Diese Vereinfachung funktioniert natürlich nur dann, wenn wir nicht noch gleichzeitig von einer anderen Klasse erben wollen.
 

Fußnoten  

1)

Diese Variante, dass man beim Aufruf eines Konstruktor die abstrakte Methode implementiert, werden wir später und ausführlicher kennen lernen. [zurück]
 
zu 16.1.4 Übungen
zur Startseite www.pohlig.de  (C) MPohlig 2005