Info B VL 8: Abstrakte Klassen & Interfaces Objektorientiere Programmierung in Java 2003 Ute Schmid (Vorlesung) Elmar Ludwig (Übung) FB Mathematik/Informatik, Universität Osnabrück Info B VL 8: Abstrakte Klassen & Interfaces – p.199 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! Info B VL 8: Abstrakte Klassen & Interfaces – p.200 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. Info B VL 8: Abstrakte Klassen & Interfaces – p.201 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.) Info B VL 8: Abstrakte Klassen & Interfaces – p.202 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 Info B VL 8: Abstrakte Klassen & Interfaces – p.203 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) Info B VL 8: Abstrakte Klassen & Interfaces – p.204 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. Info B VL 8: Abstrakte Klassen & Interfaces – p.205 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! Info B VL 8: Abstrakte Klassen & Interfaces – p.206 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! Info B VL 8: Abstrakte Klassen & Interfaces – p.207 Unterklasse extends Oberklasse Klasse implements Interface Implementation eines Interfaces 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 Info B VL 8: Abstrakte Klassen & Interfaces – p.208 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). Info B VL 8: Abstrakte Klassen & Interfaces – p.209 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) Info B VL 8: Abstrakte Klassen & Interfaces – p.210 Beispiel ‘Shapes’ Beispielcode: CShapeTest.java Shape instanceof Interface/Klasse Object Wenn eine Klasse ein Interface implementiert, können Objekte dieser Klasse an eine Variable vom Typ des Interfaces zugewiesen werden. <<interface>> Centered Circle CenteredCircle Rectangle CenteredRectangle Square CenteredSquare Info B VL 8: Abstrakte Klassen & Interfaces – p.211 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. Info B VL 8: Abstrakte Klassen & Interfaces – p.212 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) Info B VL 8: Abstrakte Klassen & Interfaces – p.213 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 } Info B VL 8: Abstrakte Klassen & Interfaces – p.214 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 {} Info B VL 8: Abstrakte Klassen & Interfaces – p.215 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. Info B VL 8: Abstrakte Klassen & Interfaces – p.216 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 { // ... } } Info B VL 8: Abstrakte Klassen & Interfaces – p.217 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; Info B VL 8: Abstrakte Klassen & Interfaces – p.218 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. Info B VL 8: Abstrakte Klassen & Interfaces – p.219 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 Info B VL 8: Abstrakte Klassen & Interfaces – p.220