29.10.2012 Aufrufe

Einführung in die Programmierung - Homepage von Peter Ziesche

Einführung in die Programmierung - Homepage von Peter Ziesche

Einführung in die Programmierung - Homepage von Peter Ziesche

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>E<strong>in</strong>führung</strong> <strong>in</strong> <strong>die</strong> <strong>Programmierung</strong><br />

Sommersemester 2005 – 2. Semester<br />

Vererbung – Beispiele zur Übung vom 04.06.2005<br />

Angenommen, es soll e<strong>in</strong>e Anwendung für e<strong>in</strong>e Universitäts-Bibliothek entwickelt werden. Als<br />

Ausleiher für Bücher und Zeitschriften kommen Studenten und Angestellte der Universität <strong>in</strong><br />

Frage. Sie werden durch zwei Klassen Student und Angestellter modelliert. Beide<br />

Klassen haben geme<strong>in</strong>same Eigenschaften wie z.B. Name, Vorname, Adresse, Telefon und<br />

E-Mail. Darüber h<strong>in</strong>aus hat jede Klasse <strong>in</strong>dividuelle Attribute: e<strong>in</strong> Student hat e<strong>in</strong>e Matrikel-<br />

Nummer und <strong>die</strong> Anzahl se<strong>in</strong>er Semester, e<strong>in</strong> Angestellter hat e<strong>in</strong>e <strong>die</strong>nstliche Telefon-Nr.<br />

In der objektorientierten <strong>Programmierung</strong> können <strong>die</strong> Geme<strong>in</strong>samkeiten beider Klassen <strong>in</strong> e<strong>in</strong>er<br />

Oberklasse Ausleiher zusammengefasst werden. Die Klassen Student und<br />

Angestellter erben <strong>von</strong> der Klasse Ausleiher. E<strong>in</strong> Vorteil der Vererbung ist, dass <strong>die</strong><br />

geme<strong>in</strong>samen Eigenschaften nur e<strong>in</strong>mal <strong>in</strong> der Oberklasse Ausleiher def<strong>in</strong>iert werden<br />

müssen. Die erbenden Klassen def<strong>in</strong>ieren nur <strong>die</strong> <strong>in</strong>dividuellen Eigenschaften.<br />

In Java wird das Erben <strong>von</strong> e<strong>in</strong>er Oberklasse mit das Schlüsselwort extends formuliert. Die<br />

drei Klassen für <strong>die</strong> Uni-Bibliothek sehen <strong>in</strong> Java wie folgt aus (es werden nicht alle oben<br />

genannten Eigenschaften def<strong>in</strong>iert):<br />

public class Ausleiher {<br />

private Str<strong>in</strong>g name;<br />

private Str<strong>in</strong>g telefon;<br />

}<br />

public Str<strong>in</strong>g getName() {<br />

return name;<br />

}<br />

public void setName( Str<strong>in</strong>g value ) {<br />

vorname = value;<br />

}<br />

public Str<strong>in</strong>g getTelefon() {<br />

return telefon;<br />

}<br />

public void setTelefon( Str<strong>in</strong>g value ) {<br />

telefon = value;<br />

}<br />

public class Student extends Ausleiher {<br />

private Str<strong>in</strong>g matrikelNr;<br />

public Str<strong>in</strong>g getMatrikelNr() {<br />

return matrikelNr;<br />

}<br />

public void setMatrikelNr( Str<strong>in</strong>g value ) {<br />

matrikelNr = value;<br />

Fachhochschule Südwestfalen<br />

<strong>Peter</strong> <strong>Ziesche</strong><br />

E-Mail: mail@peterziesche.de


}<br />

}<br />

public class Angestellter extends Ausleiher {<br />

private Str<strong>in</strong>g <strong>die</strong>nstNummer;<br />

}<br />

public Str<strong>in</strong>g getDienstNummer() {<br />

return <strong>die</strong>nstNummer;<br />

}<br />

public void setDienstNummer( Str<strong>in</strong>g value ) {<br />

<strong>die</strong>nstNummer = value;<br />

}<br />

Von allen drei Klassen können Objekte erzeugt werden. E<strong>in</strong>e Referenz vom Typ Ausleiher<br />

kann auf Objekte aller drei Klassen zeigen. Die folgenden Codezeilen s<strong>in</strong>d also korrekt:<br />

Student s1 = new Student();<br />

Angestellter a1 = new Angestellter();<br />

Ausleiher ausleiher1 = s1; //e<strong>in</strong> Student ist e<strong>in</strong> Ausleiher<br />

Ausleiher ausleiher2 = a1; //e<strong>in</strong> Angestellter ist e<strong>in</strong> Ausleiher<br />

Über <strong>die</strong> Referenz ausleiher1 kann nur auf <strong>die</strong> Eigenschaften und Methoden des<br />

Student-Objekts zugegriffen werden, <strong>die</strong> <strong>in</strong> der Oberklasse Ausleiher def<strong>in</strong>iert s<strong>in</strong>d. Es<br />

gilt nämlich: der Typ der Referenz legt fest, auf welche Eigenschaften und Methoden e<strong>in</strong>es<br />

Objekts zugegriffen werden kann, unabhängig da<strong>von</strong>, <strong>von</strong> welcher Klasse das Objekt tatsächlich<br />

ist. Für <strong>die</strong> oben erzeugten Objekte gilt:<br />

ausleiher1.setName( "Meier" ); //korrekt;<br />

ausleiher1.setMatrikelNummer( "3034713" ); //Fehler<br />

ausleiher2.setDienstNummer( "456-3248" ); //Fehler<br />

Der Vorteil <strong>von</strong> Vererbung wird deutlich, wenn man folgende Funktion implementiert: Es soll<br />

e<strong>in</strong>e Liste der Ausleiher gedruckt werden, bei denen Bücher oder Zeitschriften überfällig s<strong>in</strong>d.<br />

Die Methode könnte so aussehen:<br />

public void druckeMahnliste( ArrayList liste ) {<br />

<strong>in</strong>t i=0;<br />

while( i < liste.size()) {<br />

Ausleiher e<strong>in</strong>Ausleiher = (Ausleiher) liste.get(i);<br />

System.out.pr<strong>in</strong>tln( e<strong>in</strong>Ausleiher.getName() );<br />

}<br />

}<br />

Für den Druck der Liste ist es unerheblich, ob es sich bei den Listenelementen um Studenten<br />

oder Angestellte handelt. Es wird lediglich der Name ausgedruckt, e<strong>in</strong>e Eigenschaft der<br />

Oberklasse Ausleiher. Natürlich muss <strong>die</strong> Liste irgendwo anders im Programm mit Objekten<br />

gefüllt werden. Innerhalb der Methode druckeMahnliste() ist <strong>die</strong> genaue Klasse der<br />

Elemente nicht wichtig.<br />

Soll später e<strong>in</strong>mal e<strong>in</strong>e weitere Sorte <strong>von</strong> Ausleihern h<strong>in</strong>zugefügt werden, z.B. Professoren,<br />

braucht <strong>die</strong> Methode druckeMahnliste() nicht verändert zu werden. Sie wird dann<br />

automatisch auch säumige Professoren ausdrucken können, wenn auch <strong>die</strong> Klasse Professor<br />

<strong>von</strong> Ausleiher erbt.


Polymorphismus<br />

Wenn <strong>die</strong> Liste der säumigen Ausleiher auch <strong>die</strong> Telefon-Nr. ausdrucken soll, ergibt sich e<strong>in</strong><br />

Problem. Zwar hat e<strong>in</strong> Angestellter auch e<strong>in</strong>e private Telefon-Nr. (e<strong>in</strong> Attribut der Klasse<br />

Ausleiher), für e<strong>in</strong>en Mitarbeiter der Bibliothek ist es aber wahrsche<strong>in</strong>lich e<strong>in</strong>facher, den<br />

Angestellten an se<strong>in</strong>em Arbeitsplatz anzurufen und um Rückgabe der entliehenen Bücher zu<br />

bitten. Bei Angestellten sollte also <strong>die</strong> Dienst-Nr. ausgedruckt werden, bei Studenten <strong>die</strong><br />

Privat-Nr.<br />

Es wäre schlechter Stil, wenn <strong>in</strong> der Methode druckeMahnliste() abgefragt würde, ob das<br />

Listen-Element e<strong>in</strong> Student- oder e<strong>in</strong> Angestellter-Objekt ist. Damit wäre <strong>die</strong> Methode<br />

wieder <strong>von</strong> der konkreten Klasse e<strong>in</strong>es Objekts abhängig. Beim späteren h<strong>in</strong>zufügen der Klasse<br />

Professor müsste druckeMahnliste() angepasst werden.<br />

In der objektorientierten <strong>Programmierung</strong> benutzt man stattdessen <strong>die</strong> Möglichkeit, Methoden<br />

e<strong>in</strong>er Oberklasse <strong>in</strong> e<strong>in</strong>er Unterklasse zu redef<strong>in</strong>ieren (statt redef<strong>in</strong>ieren wird häufig auch der<br />

Begriff überschreiben verwendet). In der Klasse Ausleiher liefert <strong>die</strong> Methode<br />

getTelefon() <strong>die</strong> Privat-Nr des Ausleihers zurück. Die Klasse Angestellter kann <strong>die</strong>se<br />

Methode so redef<strong>in</strong>ieren, dass stattdessen <strong>die</strong> Dienst-Nr. zurückgeliefert wird:<br />

public class Angestellter extends Ausleiher {<br />

private Str<strong>in</strong>g <strong>die</strong>nstNummer;<br />

}<br />

public Str<strong>in</strong>g getTelefon() {<br />

return <strong>die</strong>nstNummer;<br />

}<br />

public void setTelefon( Str<strong>in</strong>g value ) {<br />

<strong>die</strong>nstNummer = value;<br />

}<br />

public Str<strong>in</strong>g getPrivatNummer() {<br />

return telefon; //Attribut <strong>von</strong> Ausleiher<br />

}<br />

public void setPrivatNummer( Str<strong>in</strong>g value ) {<br />

telefon = value;<br />

}<br />

Die <strong>von</strong> Ausleiher geerbten Methoden getTelefon() und setTelefon() werden also<br />

<strong>in</strong> der Klasse Angestellter so verändert, dass sie <strong>die</strong> <strong>die</strong>nstliche Nummer zurückliefern<br />

oder setzen. Die Klasse Angestellter erhält zusätzlich noch <strong>die</strong> Methoden<br />

getPrivatNummer() und setPrivatNummer(), mit denen gezielt auf das geerbte<br />

Attribut zugegriffen werden kann.<br />

Die Methode druckeMahnliste() kann nahezu unverändert bleiben:<br />

public void druckeMahnliste( ArrayList liste ) {<br />

<strong>in</strong>t i=0;<br />

while( i < liste.size()) {<br />

Ausleiher e<strong>in</strong>Ausleiher = (Ausleiher) liste.get(i);<br />

System.out.pr<strong>in</strong>tln( e<strong>in</strong>Ausleiher.getName() +<br />

" " + e<strong>in</strong>Ausleiher.getTelefon());<br />

}<br />

}


Durch <strong>die</strong> Redef<strong>in</strong>ition der Methode getTelefon() <strong>in</strong> der Klasse Angestellter wird<br />

jetzt bei Studenten <strong>die</strong> Private-Nr. und bei Angestellten <strong>die</strong> Dienst-Nr. ausgedruckt. Die<br />

virtuelle Masch<strong>in</strong>e <strong>von</strong> Java kümmert sich automatisch im H<strong>in</strong>tergrund darum, dass bei<br />

Student-Objekten getTelefon() <strong>von</strong> Ausleiher und bei Angestellter-Objekten<br />

getTelefon() <strong>von</strong> Angestellter aufgerufen wird. Diese automatische Auswahl <strong>von</strong><br />

Methoden wird als Polymorphismus bezeichnet.<br />

Object<br />

Wenn e<strong>in</strong>e Java-Klasse <strong>von</strong> ke<strong>in</strong>er anderen Klasse erbt, also ke<strong>in</strong> extends angibt, fügt der<br />

Java-Compiler bei der Übersetzung der .java-Datei automatisch extends Object e<strong>in</strong>. Jede<br />

Klasse erbt also direkt oder <strong>in</strong>direkt immer <strong>von</strong> Object.<br />

Dies hat zur Folge, dass Referenzen vom Typ Object auf beliebige Objekte zeigen können<br />

(man mache sich <strong>die</strong>s e<strong>in</strong>dr<strong>in</strong>glich klar). Die folgenden Zeilen s<strong>in</strong>d also korrekt:<br />

Angestellter an = new Angestellter();<br />

ArrayList al = new ArrayList();<br />

Str<strong>in</strong>g s = "Hallo TBW";<br />

Object objekt1 = an;<br />

Object objekt2 = al;<br />

Object objekt3 = s;<br />

Benutzt wird <strong>die</strong>s vor allem <strong>in</strong> Conta<strong>in</strong>er-Klassen, wie z.B. ArrayList. Da <strong>in</strong> der Liste<br />

beliebige Objekte gespeichert werden sollen, speichert e<strong>in</strong>e ArrayList e<strong>in</strong>fach Referenzen<br />

vom Typ Object und kann damit auf jedes Java-Objekt verweisen.<br />

Schnittstellen<br />

In der dritten Übung wurden Sortier-Algorithmen entwickelt. Sie waren <strong>in</strong> der Lage, e<strong>in</strong>e Liste<br />

<strong>von</strong> Büchern aufsteigend nach Preisen zu sortieren. Man betrachte nun den folgenden Sortier-<br />

Algorithmus (Bubblesort) und achte darauf, an welchen Stellen der Algorithmus da<strong>von</strong><br />

abhängig ist, dass Bücher nach Preisen sortiert werden sollen. Diese Stellen s<strong>in</strong>d fett gedruckt.<br />

public static void sortiere(ArrayList liste) {<br />

<strong>in</strong>t j = liste.size()-1;<br />

while( j>0) {<br />

<strong>in</strong>t i=0;<br />

while( i < j) {<br />

Buch b1 = (Buch) liste.get(i);<br />

Buch b2 = (Buch) liste.get(i+1);<br />

}<br />

if(b1.Preis > b2.Preis) {<br />

//swappen<br />

//Element i mit Element i+1 tauschen<br />

liste.set( i, b2);<br />

liste.set( i+1, b1);<br />

}<br />

i++;<br />

}<br />

j--;<br />

}


Abgesehen <strong>von</strong> den drei markierten Stellen ist der Algorithmus unabhängig da<strong>von</strong>, dass Bücher<br />

nach Preisen sortiert werden sollen. Der Rest des Algorithmus bliebe unverändert, wenn z.B.<br />

Studenten nach Matrikel-Nr. sortiert werden sollten.<br />

Um <strong>die</strong> Abhängigkeit <strong>von</strong> der Klasse Buch zu entfernen, braucht nur Buch durch Object<br />

ersetzt zu werden. Durch <strong>die</strong> Verwendung <strong>von</strong> Object-Referenzen kann der Algorithmus mit<br />

beliebigen Objekten umgehen (siehe oben).<br />

Allerd<strong>in</strong>gs ist es dann nicht mehr ohne weiteres möglich, <strong>die</strong> Preise zweier Bücher mite<strong>in</strong>ander<br />

zu vergleichen, denn Object hat ke<strong>in</strong>e Eigenschaft Preis und der Typ der Referenz legt fest,<br />

auf welche Eigenschaften und Methoden zugegriffen werden kann. Zur Lösung <strong>die</strong>ses Problems<br />

bietet Java Schnittstellen an. Schnittstellen s<strong>in</strong>d ähnliche Konstrukte wie Klassen, enthalten aber<br />

nur Methoden-Def<strong>in</strong>itionen ohne Implementierung. Für den Sortier-Algorithmus muss e<strong>in</strong>e<br />

Schnittstelle def<strong>in</strong>iert werden, über <strong>die</strong> zwei Objekt mite<strong>in</strong>ander verglichen werden können.<br />

public <strong>in</strong>terfache Vergleicher {<br />

}<br />

//-1 wenn objekt1 kle<strong>in</strong>er als objekt2 ist<br />

// 0 wenn beide Objekte gleich s<strong>in</strong>d<br />

//+1 wenn objekt 1 größer als objekt2 ist<br />

<strong>in</strong>t vergleiche( Object objekt1, Object objekt2 );<br />

Für den Preisvergleich zwischen zwei Büchern wird e<strong>in</strong>e Klasse geschrieben, <strong>die</strong><br />

Vergleicher implementiert:<br />

public class BuchPreisVergleicher implements Vergleicher {<br />

public <strong>in</strong>t vergleiche( Object objekt1, Object objekt2 ) {<br />

Buch buch1 = (Buch) objekt1;<br />

Buch buch2 = (Buch) objekt2;<br />

if( buch1.Preis < buch2.Preis ) {<br />

return -1;<br />

}<br />

else if( buch1.Preis > buch2.Preis ) {<br />

return 1;<br />

}<br />

return 0;<br />

}<br />

}<br />

Die Klasse BuchPreisVergleicher stellt für <strong>die</strong> <strong>in</strong> der Schnittstelle def<strong>in</strong>ierte Methode<br />

vergleiche() e<strong>in</strong>e Implementierung zur Verfügung, <strong>die</strong> zwei Bücher bezüglich ihrer Preise<br />

vergleicht und das billigere als kle<strong>in</strong>eres zurückliefert. E<strong>in</strong>e andere Implementierung <strong>von</strong><br />

Vergleicher könnte Bücher nach Titel oder Autor sortieren oder Objekte e<strong>in</strong>er ganz anderen<br />

Klasse mite<strong>in</strong>ander vergleichen. Um z.B. Studenten nach Matrikel-Nr. (siehe oben) absteigend<br />

zu sortieren benötigt man folgende Klasse, mit der Sortier-Algorithmus unverändert auch zu<br />

Sortieren <strong>von</strong> Student-Objekten verwendet werden kann:<br />

public class StudentVergleicher implements Vergleicher {<br />

public <strong>in</strong>t vergleiche( Object objekt1, Object objekt2 ) {<br />

Student s1 = (Student) objekt1;<br />

Student s2 = (Student) objekt2;<br />

return s1.getMatrikelnr().compareTo( s2.getMatrikelNr());<br />

}<br />

}


Die Methode compareTo() ist <strong>in</strong> der Klasse Str<strong>in</strong>g def<strong>in</strong>iert. Sie vergleicht zwei Str<strong>in</strong>gs<br />

mite<strong>in</strong>ander und liefert den als kle<strong>in</strong>eren zurück, der bei alphabetischer Sortierung als erster<br />

kommt.<br />

Der Sortier-Algorithmus wird so umgeschrieben, dass er als Parameter neben der Liste auch<br />

e<strong>in</strong>e Referenz auf e<strong>in</strong> Vergleicher-Objekt erhält. Dieses Objekt benutzt der Algorithmus,<br />

um das kle<strong>in</strong>ere <strong>von</strong> zwei Objekten zu ermitteln. Es spielt dann ke<strong>in</strong>e Rolle mehr, ob Bücher,<br />

Studenten oder andere Objekte sortiert werden sollen. Man benötigt nur noch e<strong>in</strong>en Sortier-<br />

Algorithmus für alle möglichen Sortierungen.<br />

public static void sortiere(ArrayList liste, Vergleicher v) {<br />

<strong>in</strong>t j = liste.size()-1;<br />

while( j>0) {<br />

<strong>in</strong>t i=0;<br />

while( i < j) {<br />

Object o1 = liste.get(i);<br />

Object o2 = liste.get(i+1);<br />

}<br />

if(v.vergleiche( o1, o2) == -1) {<br />

//swappen<br />

//Element i mit Element i+1 tauschen<br />

liste.set( i, b2);<br />

liste.set( i+1, b1);<br />

}<br />

i++;<br />

}<br />

j--;<br />

}<br />

Schnittstellen werden im Java-API häufig e<strong>in</strong>gesetzt, um Standard-Klassen e<strong>in</strong> spezifisches<br />

Verhalten zu verleihen. Zum Beispiel gibt es e<strong>in</strong>e Klasse MouseListener. Sie def<strong>in</strong>iert<br />

Methoden, mit denen auf e<strong>in</strong>en Tastendruck der Maus reagiert werden kann. E<strong>in</strong>e konkrete<br />

Anwendung enthält e<strong>in</strong>e Klasse, <strong>die</strong> MouseListener implementiert. E<strong>in</strong> Fenster, e<strong>in</strong> Objekt<br />

der Java-API-Klasse W<strong>in</strong>dow, ruft bei jedem Mausklick <strong>die</strong> Methoden der Schnittstelle auf.<br />

E<strong>in</strong>e Anwendung kann dadurch <strong>in</strong>dividuell auf Mausklicks reagieren, ohne e<strong>in</strong>e eigene Fenster-<br />

Klasse schreiben zu müssen.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!