Interfaces

Werbung
10 Interfaces
10 - 1
Interfaces
Inhalt
Interfaces
Abstrakte Klassen
Enumeration
Übungsaufgaben
Interfaces
In C++ kann eine Klasse Kind von zwei Eltern sein. Dieses Konzept der mehrfachen
Vererbung wurde in Java nicht übernommen, da es zu viele Schwierigkeiten mit sich bringt.
Statt dessen kann eine Klasse von einer Vaterklasse und mehreren Interface-Klassen erben.
Ein Interface ist wie eine Klasse, nur daß alle Methoden keinen Funktionskörper haben. Sie
werden sozusagen nur abstrakt deklariert. Ein Interface dient also nur dazu anzugeben, daß
eine Klasse gewisse Methoden implementiert. Wenn eine Klasse ein Interface erbt, sagt man,
es implementiert das Interface. Ein Interface hat keine Eigenschaften (Variablen).
Natürlich muß jede Klasse, die das Interface implementiert, dessen Methoden tatsächlich
deklarieren.
Als Beispiel schreiben wir eine Quicksort-Routine, die beliebige Objekte sortieren kann.
Dazu muß diese Routine die Objekte vergleichen können. Die Objekte selber bringen die
Methode zum Vergleich mit. Die Quicksort-Routine muß wissen, daß die Objekte diese
Methode implementieren. Hier ist zunächst einmal die Definition des Interface.
interface SortObject
{ public int compare (SortObject o);
}
Wie man sieht wird der Funktionskörper von compare durch ein Semikolon ; ersetzt. Man
kann nun eine beliebige Klasse sortierbar machen, wenn man compare implementiert. Wir
wollen double-Werte sortieren. Dazu schreiben wir eine neue Klasse SortDouble.
class SortDouble implements SortObject
{
private double X;
public SortDouble (double x)
{
X=x;
}
public double getValue ()
{
return X;
}
public int compare (SortObject o)
10 Interfaces
{
10 - 2
SortDouble s=(SortDouble)o;
if (X<s.X) return -1;
else if (X==s.X) return 0;
else return 1;
}
}
Diese Klasse kann einen double-Wert aufnehmen und implementiert die compare-Methode.
Nun folgt das Hauptprogramm, das eine statische Funktion Sorter.sort() aufruft.
public class Test
{
public static void main (String args[])
{
int i,n=1000;
SortDouble x[]=new SortDouble[n];
for (i=0; i<n; i++) x[i]=new SortDouble(Math.random());
Sorter.sort(x);
for (i=1; i<n; i++)
if (x[i].getValue()<x[i-1].getValue())
System.out.println("Fehler beim Sortieren");
}
}
Die Methode Sorter.sort weiß nun nichts von double-Werten, sondern benutzt nur
compare aus dem Interface SortObject.
class Sorter
{
static public void sort (SortObject v[])
{
QuickSort(v,0,v.length-1);
}
static private void QuickSort (SortObject a[], int lo0, int hi0)
{
int lo=lo0;
int hi=hi0;
SortObject mid;
if (hi0>lo0)
{
mid = a[(lo0+hi0)/2];
while(lo<=hi)
{
while((lo<hi0) && (a[lo].compare(mid)<0))
++lo;
while((hi>lo0) && (a[hi].compare(mid)>0))
--hi;
if(lo<=hi)
{
swap(a,lo,hi);
++lo;
--hi;
}
}
if(lo0<hi) QuickSort(a,lo0,hi);
if(lo<hi0) QuickSort(a,lo,hi0);
}
}
static private void swap (SortObject a[], int i, int j)
{
SortObject T;
T=a[i];
a[i]=a[j];
a[j]=T;
}
10 Interfaces
10 - 3
}
Wie man sieht, sind Interfaces für verschiedene Zwecke nützlich.
Abstrakte Klassen
Tatsächlich ist es auch möglich, Klassen abstrakt zu deklarieren. Dazu erhalten sie das
Schlüsselwort abstract, ebenso wie alle Methoden, die nicht mit einem Funktionskörper
ausgestattet sind.
Ein Beispiel ist
abstract class GraphicsObject
{
...
abstract void paint (Graphics g);
...
}
Diese Klasse enthält die abstrakte Methode paint und muß daher selber abstrakt sein. Erst
die Kinder von GraphicsObject sollen die paint-Methode implementieren. Es kann dann
von GraphicsObject selbst auch keine Instanz angelegt werden, nur von seinen Kindern.
Dennoch kann man etwa einen Vektor mit Instanzen von Kindern von GraphicsObject
füllen und einfach die paint-Methode von GraphicsObject verwenden.
Die Alternative ist, alle Kinder ein Interface GraphicsObject implementieren zu lassen.
Dies ist aber nicht so logisch und hat außerdem den Nachteil, daß Interfaces keine Variablen
vererben können.
Enumeration
Eine andere Anwendung ist das Interface Enumeration, das verschiedene Klassen verwenden.
Es stellt zwei einfache Methoden zur Verfügung, um durch alle gespeicherten Objekte in
einer Schleife zu gehen. Diese Methoden sind hasMoreElements und nextElement.
Die Klasse Properties (ein Kind von Hashtabe) liefert mit der Methode propertyNames()
eine Enumeration aller definierten Schlüssel zurück. Man kann damit alls Schlüssel und Ihre
Werte auflisten. Im folgenden Beispiel tun wir dies für die System-Properties.
import java.util.*;
public class Test
{
public static void main (String args[])
{
Properties p=System.getProperties();
Enumeration e=p.propertyNames();
while (e.hasMoreElements())
{
String s=(String)e.nextElement();
System.out.println(s+": "+(String)p.get(s));
}
}
}
Die Ausgabe ist hier
user.language: de
10 Interfaces
10 - 4
java.home: C:\JDK1.1.6\BIN\..
java.vendor.url.bug: http://java.sun.com/cgi-bin/bugreport.cgi
awt.toolkit: sun.awt.windows.WToolkit
file.encoding.pkg: sun.io
java.version: 1.1.6
file.separator: \
line.separator:
user.region: DE
file.encoding: 8859_1
java.compiler: symcjit
java.vendor: Sun Microsystems Inc.
user.timezone: ECT
user.name: Rene
os.arch: x86
os.name: Windows 95
java.vendor.url: http://www.sun.com/
user.dir: D:\java\kurs
java.class.path:
.;C:\JDK1.1.6\BIN\..\classes;C:\JDK1.1.6\BIN\..\lib\classes.zip;C:\JDK1.1.6
\BIN\..\lib\classes.jar;C:\JDK1.1.6\BIN\..\lib\rt.jar;C:\JDK1.1.6\BIN\..\li
b\i18n.jar
java.class.version: 45.3
os.version: 4.0
path.separator: ;
user.home: C:\JDK1.1.6\BIN\..
Z.B. gibt die Systemeigenschaft file.separator an, welches Zeichen zur Trennung von
Filenamen und Verzeichnis benutzt wird. Bei UNIX ist dies /.
Beispiel
Wir wollen ein Interface für unsere StringList-Klasse entwickeln. Als Objekt, das die
Enumeration implementiert, verwenden wir einfach die StringList selbst. Eine Methode
elements() gibt die StringList als Enumeration zurück.
class StringList implements Enumeration
{
...
private StringElement Next=null;
...
public boolean hasMoreElements ()
{
return Next!=null;
}
public Object nextElement ()
{
if (Next==null) return null;
String s=Next.getString();
Next=(StringElement)Next.getNext();
return s;
}
public Enumeration elements ()
{
if (Next!=null) return null; // Enumeration aktiv!
Next=First;
return this;
}
}
public class Test
{
public static void main (String args[])
10 Interfaces
{
10 - 5
StringList L=new StringList();
for (int i=0; i<10; i++) L.append("Number "+i);
Enumeration e=L.elements();
while (e.hasMoreElements())
{
System.out.println((String)e.nextElement());
}
}
}
Der Nachteil dieser Konstruktion ist, daß elements() versagt, während die Enumeration
noch nicht abgearbeitet ist. Wenn es sein kann, daß zwei Aufzählungen der Liste gleichzeitig
benötigt werden, so muß man ein separates Objekt erzeugen.
class StringList
{
...
public Enumeration elements ()
{
return new StringListEnumeration(First);
}
}
class StringListEnumeration implements Enumeration
{
StringElement Next;
public StringListEnumeration (StringElement first)
{
Next=first;
}
public boolean hasMoreElements ()
{
return Next!=null;
}
public Object nextElement ()
{
if (Next==null) return null;
String s=Next.getString();
Next=(StringElement)Next.getNext();
return s;
}
}
Auch dieser Ansatz ist nicht ganz sicher, da sich ja die Liste ändern kann, während die
Enumeration abgearbeitet wird. Hier hilft dann nur eine Kopie, z.B. auf einen Vector (der
selber eine elements-Methode hat).
Übungsaufgaben
1. Schreiben Sie StringList.elements() um, so daß es die Strings auf einen
Vector kopiert und dessen elements-Methode benutzt.
Lösungen.
Aufgaben ohne Lösung
1. Testen Sie die elements()-Methode von Hashtable anhand der SystemProperties.
Herunterladen