Info B VL 8: Abstrakte Klassen &
Interfaces
Objektorientiere Programmierung in Java 2003
Ute Schmid (Vorlesung) Elmar Ludwig ( ¨Ubung)
FB Mathematik/Informatik, Universit¨at Osnabr¨uck
Motivation: Abstrakte Klassen
Illustration: myshapes2
Implementation verschiedener Klassen für
geometrische Figuren: Circle, Rectangle, Square, Ellipse, Triangle
Arbeiten mit einem Array von Shape-Objekten: Es wäre günstig, wenn eine gemeinsame Oberklasse Shape existiert, die alle Komponenten definiert, die allen geometrischen Formen gemeinsam sind.
Was ist mit den Methoden area() und circumference()?
(Ohne konkrete geometrische Gestalt ist Berechnungsvorschrift unbekannt.)
abstrakte Methoden!
Abstrakte Klassen und Methoden (1)
public abstract class Shape {
public abstract double area();
}
Abstrakte Methode: Definition ohne Implementation (Körper); Modifikator abstract; Methodenkopf
abgeschlossen durch Semikolon.
Jede Klasse, die eine abstrakte Methode enthält, ist selbst abstrakt und muss als abstract deklariert werden.
Eine abstrakte Klasse kann nicht instantiiert werden (keine Objekt-Erzeugung mit new möglich).
Eine Unterklasse einer abstrakten Klasse kann nur
instantiiert werden, wenn alle abstrakten Methoden der Oberklasse implementiert werden.
Abstrakte Klassen und Methoden (2)
Eine Unterklasse, die nicht alle abstrakten Methoden implementiert, ist selbst abstrakt.
static- und final-Methoden können nicht abstrakt sein, da diese nicht von einer Unterklasse
überschrieben werden können.
private Methoden sind implizit final. Ebenso können final Klassen keine abstrakten Methoden enthalten.
Klassen können abstract deklariert werden, auch wenn sie keine abstrakten Methoden enthalten.
Hinweis, dass Methoden unvollständig sind und dass die Methode als Oberklasse für konkrete Unterklassen gedacht ist.
(Abstrakte Klassen können generell nicht instantiiert werden.)
Abstrakte Klassen und Methoden (3)
Objekte von Unterklassen können direkt (ohne Cast) an Variablen (z. B. Elemente eines Arrays von Shapes) der Oberklasse zugewiesen werden.
Abstrakte Methoden der Oberklasse können für jedes Objekt einer konkreten Unterklasse aufgerufen werden (dynamic method lookup).
Beispiel: ShapeTest.java
Motivation: Interfaces
Nächste Erweiterung des Shapes-Beispiels: Nicht nur Grösse, auch Position der geometrischen Objekte in der Ebene.
Erste Idee: weitere abstrakte Klasse CenteredShape mit Unterklassen CenteredCircle,
CenteredRectangle, ...
CenteredCircle soll natürlich auch die Methoden von Circle erben.
Problem: Java erlaubt nicht, dass eine Klasse mehr als eine Oberklasse hat! (Mehrfachvererbung)
Java Lösung: Interfaces (Schnittstellen)
Interfaces (1)
public interface Centered {
public void setCenter(double x, double y);
public double getCenterX();
public double getCenterY();
}
Eine Klasse kann beliebig viele Interfaces implementieren.
Ein Interface ist ein Referenztyp sehr ähnlich einer Klasse:
Definiert wird eine Funktionalität und nicht eine Implementation (Realisierung)
ein Interface gibt Signaturen – Namen und Typen von Methoden (und Konstanten) – vor.
Interfaces (2)
Ein Interface wird mit dem Schlüsselwort interface deklariert.
Ein Interface enthält keinerlei Methoden-Implementation.
Alle Methoden sind implizit abstrakt, auch wenn ohne diesen Modifikator deklariert.
Ein Interface kann nur Instanz-Methoden enthalten.
Ein Interface ist ohne Sichtbarkeitsmodifikator
paketsichtbar. Als einziger Sichtbarkeitsmodifikator darf public angegeben werden. Alle Methoden sind implizit public, auch wenn der Modifikator nicht
explizit angegeben ist.
Es ist ein Fehler, protected oder private Methoden in einem Interface zu deklarieren!
Interfaces (3)
Ein Interface kann keine Instanz-Felder definieren, aber als static und final deklarierte Konstanten.
Da ein Interface nicht instantiiert werden kann, definiert es keinen Konstruktor.
Interfaces sind reine Spezifikationen!
Implementation eines Interfaces
Unterklasse extends Oberklasse Klasse implements Interface
Schlüsselwort implements folgt nach extends (falls Oberklasse angegeben); wird von einem oder
mehreren (durch Komma getrennte) Namen von Interfaces gefolgt.
implements bedeutet, dass in der
implementierenden Klasse die Körper für Methoden des Interfaces definiert werden. Werden nicht alle
Methoden implementiert, so ist die Klasse abstrakt und muss als solche deklariert werden. (Alle Methoden des Interfaces werden Teil der Klasse.)
Beispielcode: CenteredRectangle.java
Interfaces und Konstanten (1)
Konstanten dürfen in Interface-Deklarationen vorkommen.
Alle Felder, die in einem Interface deklariert werden, werden implizit als static und final aufgefasst, auch wenn nicht explizit so deklariert.
Es ist jedoch guter Stil, diese Modifikatoren explizit anzugeben!
Jede Klasse, die das Interface implementiert, erbt die Konstanten und kann sie benutzen, als wären sie
direkt in der Klasse selbst deklariert (keine Voranstellung des Interface-Namens vor den Konstanten-Namen notwendig).
Interfaces und Konstanten (2)
Konstanten müssen nicht unbedingt mit festen Werten initialisiert werden:
public interface RandVals {
int rint = (int) (Math.random() * 10);
long rlong = (long) (Math.random() * 10);
float rfloat = (float) (Math.random() * 10);
double rdouble = Math.random() * 10;
}
Manchmal nützlich: Interface, das nur Konstanten enthält.
Konstanten, die von mehreren Klassen benutzt werden (wie Port-Nummern, die von Client und Server benutzt werden).
Beispiel: java.io.ObjectStreamConstants
(Konstanten für Javas Serialisierungs-Mechanismus)
Beispiel ‘Shapes’
Beispielcode: CShapeTest.java
Wenn eine Klasse ein Interface implementiert, können Objekte dieser Klasse an eine Variable vom Typ des Interfaces zugewiesen werden.
Object instanceof Interface/Klasse
<<interface>>
Centered
Square
CenteredCircle
CenteredRectangle
CenteredSquare Rectangle
Circle Shape
Interfaces vs. Abstrakte Klassen (1)
Entwurfsentscheidung zwischen abstrakter Klasse und Interface.
Interface: Jede Klasse kann es implementieren.
Zwei nicht verwandte Klassen können dasselbe Interface implementieren.
Abstrakte Klasse: Nur eine Klasse kann Oberklasse einer anderen Klasse sein.
Interface: Nur abstrakte Methoden; wenn Methoden für viele Klassen gleich sind, so müssen sie immer neu implementiert werden.
Abstrakte Klasse: Kann Default-Implementation für typische Methoden liefern.
Interfaces vs. Abstrakte Klassen (2)
Kompatibilität:
Wenn Interface zum public API hinzugefügt wird und später das Interface um eine Methode erweitert wird, so sind alle Klassen, die das Interface implementieren,
“kaputt”.
Zu abstrakten Klassen können implementierte Methoden gefahrlos hinzugefügt werden.
Manchmal nützlich: Abstrakte Klasse, die ein Interface implementiert und Default-Implementationen für
Methoden der Unterklassen liefert. (Adapter-Klasse)
Implementation mehrerer Interf.
Wenn die Klasse, die mehrere Interfaces
implementiert, nicht abstrakt sein soll, so müssen die Methoden aller Interfaces implementiert werden.
public class SuperDuperSquare extends Shape implements Centered, Scalable {
// Methods omitted }
Erweitern von Interfaces
Wie Klassen Unterklassen haben können, so können Interfaces Unter-Interfaces haben.
Bei Interfaces dürfen hinter extends mehrere andere Interfaces stehen.
public interface Positionable extends Centered {
public void setUpperRightCorner(double x, double y);
public double getUpperRightX();
public double getUpperRightY();
}
public interface Transformable extends
Scalable, Translatable, Rotatable {}
public interface SuperShape extends
Positionable, Transformable {}
Probleme
Probleme ähnlich zu Mehrfachvererbung
Regeln, wenn Methoden gleichen Namens in
verschiedenen (zu implementierenden, erweiternden) Interfaces vorkommen:
Methoden mit gleichem Namen und gleicher Signatur werden einmal aufgenommen.
Methoden mit gleichem Namen und verschiedenen Signaturen sind überladen.
Methoden mit gleichem Namen, gleichen
Parametern und verschiedenem Rückgabetyp führen zu Übersetungs-Fehler.
Bei Methoden mit gleicher Signatur und
verschiedenen spezifizierten Exceptions muss die
“Schnittmenge” dieser Exceptions oder eine Teilmenge davon spezifiziert werden.
Beispiel
Übersetzungsfehler:
interface X {
void setup() throws FileNotFoundException;
}
interface Y {
void setup() throws IOException;
}
// Schnittmenge ist FileNotFoundException class Z implements X, Y {
public void setup() throws IOException { // ...
}
Marker-Interfaces
Manchmal ist es nützlich, ein leeres Interface zu definieren.
Klasse, die dieses Interface implementiert, muss keine Methoden implementieren.
Jede Instanz der Klasse ist zulässige “Instanz” des Interfaces.
Prüfbar mit instanceof Beispiel: Cloneable
MyClass o; // Initialized elsewhere MyClass copy;
if (o instanceof Cloneable) copy = o.clone();
else copy = null;
Enumeration-Interface
Idee: Für eine Datenstruktur, die Elemente hält, sollen diese Elemente aufgezählt werden.
Typisch für Collection-Klassen, in denen Objekte gehalten werden.
Beispiele: Stack, LinkedList
Vordefiniertes Enumeration-Interface mit zwei Methoden:
boolean hasMoreElements(): true, wenn die Aufzählung noch weitere Elemente enthält, false sonst
Object nextElement(): liefert das nächste Element, falls es existiert, sonst wird eine
Exception ausgelöst.
Beispiel
Beispielcode: ListEnumerator.java
Eine unelegante Art, den Inhalt einer Liste aufzuzählen, ist es, mit einer Schleife über die Liste zu laufen:
MyList intlist = new MyList();
for (int i=0; i < 10; i++) { intlist.add(new Integer(i));
}
// enumerate elements by index
for (int i=0; i < intlist.size(); i++) { System.out.println(intlist.get(i));
besser: Implementation einer Enumeration