• Keine Ergebnisse gefunden

Begriffsklärung:(Objektgeflecht)Eine Menge von Objekten, die sich gegenseitig referenzieren, nennen wir ein

N/A
N/A
Protected

Academic year: 2022

Aktie "Begriffsklärung:(Objektgeflecht)Eine Menge von Objekten, die sich gegenseitig referenzieren, nennen wir ein"

Copied!
12
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 502

Begriffsklärung: (Objektgeflecht)

Eine Menge von Objekten, die sich gegenseitig referenzieren, nennen wir ein Objektgeflecht (vgl. Folie 339).

Bemerkung:

• Objektgeflechte werden zur Laufzeit aufgebaut und verändert, sind also dynamische Entitäten.

• Klassendiagramme kann man als vereinfachte statische Approximationen von Objektgeflechten verstehen.

Lebensdauer von Objekten und Instanzvariablen:

In Java lassen sich Objekte nicht löschen.

Aus Sicht des Programmierers leben Objekte und deren Instanzvariablen von der Objekterzeugung bis zum Ende der Ausführung des Programms.

Der Speicher nicht erreichbarer Objekte wird ggf.

vor Ablauf der Lebensdauer von der automatischen Speicherbereinigung frei gegeben (vgl. Folien 386ff).

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 503

Attributzugriff

Syntax in Java:

Auf Instanzvariablen von Objekten kann mit Ausdrücken folgender Form zugegriffen werden:

<referenzwertiger Ausdruck> <Attributbezeichner>

Semantik:

Werte den referenzwertigen Ausdruck aus. Liefert diesernull, löse eine NullPointerException aus.

Andernfalls liefert er die Referenz auf ein Objekt X;

in dem Fall liefert der gesamte Ausdruck die Instanzvariable von X zum angegenen Attribut (L- Wert) oder deren Wert (R-Wert).

Abkürzende Notation:

Der implizite Methodenparameterthiskann beim Zugriff auf eine Attribut a weggelassen werden, d.h.

a

ist gleichbedeutend mit this.a

innerhalb von Klassen, in denenadeklariert ist.

Beispiel: (Attributzugriffe)

class DeinObjekt { MeinObjekt du;

String deinName;

}

class MeinObjekt {

boolean binEingetragen;

String meinName;

void dichInit (DeinObjekt d) {

System.out.println( d.deinName );

d.deinName = this.meinName;

this.binEingetragen = true;

meinName = d.du.meinName;

} }

Methodenaufruf: (engl. method invocation)

Syntax:

Ein Methodenaufruf ist ein Ausdruck ähnlich einem Prozeduraufruf, allerdings mit einem zusätzlichen Parameter:

<refwertigerAusdruck>.<Methodenbezeichner> (

<AktuelleParameterliste> ) Semantik:

Werte den referenzwertigen Ausdruck aus. Liefert diesernull, löse eine NullPointerException aus.

Andernfalls liefert er die Referenz auf ein Objekt X.

Werte die aktuellen Parameter p1, ..., pn aus.

Führe den Rumpf der angegebenen Methode mit - X als implizitem Parameter und

- p1, ... , pn als expliziten Parametern aus.

Das Ergebnis des Aufrufs ist der Rückgabewert der Ausführung des entsprechenden Methodenrumpfes.

Bemerkung:

Eine verfeinerte Semantik wird in 5.3 behandelt.

Dabei wird auch der Zusammenhang zum Senden von Nachrichten angesprochen.

(2)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 506

Beispiel: (Methodenaufrufe)

class Mensch {

Mensch vater, mutter;

String name;

Mensch getOpa (boolean mutterseits) { if ( mutterseits ) {

return mutter.vater;

} else {

return vater.vater;

} }

void eineMethode (Mensch m) { Mensch opaV;

String opaMName;

opaV = m.getOpa(false);

opaMName = m.getOpa(true). name;

} ...

}

Abkürzende Notation:

Wie beim Attributzugriff kann auch beim Methoden- aufruf der implizite Methodenparameterthisweg- gelassen werden, also m(...)statt this.m(...).

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 507

Objektorientierte Programme

Ein objektorientiertes Java-ProgrammΠ besteht aus einer Menge von Klassen. Mindestens eine der Klassen muss eine Methode mit Namen main und folgender Signatur besitzen:

public static void main ( String[] args ) {

...

}

Beim Start vonΠwird die Klasse angegeben, deren main-Methode ausgeführt werden soll:

java <Klassenname> <arg1> <arg2> ...

Die Argumente arg1,... werden dabei im Parameter args als ein Feld von Strings übergeben.

Bei der Ausführung werden die benötigten Objekte erzeugt. Diese Bearbeiten ihre Aufträge durch Ausführung von Methoden.

Initialisierung

Attribute und lokale Variablen können direkt an ihrer Deklarationsstelle initialisiert werden.

Somit sind folgende Programmstücke gleichbedeutend.

float pi;

pi = 3.141;

float pi = 3.141;

In Java können Attribute und Variablen durch das Schlüsselwortfinalals unveränderlich

deklariert werden.

In diesem Fall muss die Initialisierung an der Deklarationsstelle erfolgen.

class Mathe { ...

final float pi = 3.141; // Konstante ...

}

Die Initialisierung von Attributen erfolgt vor dem Eintritt in den Konstruktorrumpf.

5.2.3 Spracherweiterungen: Initialisierung

und Ausnahmebehandlung Beispiel: (Initialisieren von Attributen)

Folgendes Programm mit Initialisierung class C {

int ax = 7, ay = 9;

C(){

m();

}

void m(){

int v1 = 4848, v2 = -3; ...

} }

ist äquivalent zu folgendem Programm class C {

int ax, ay;

C(){

ax = 7;

ay = 9;

m();

}

void m(){

int v1, v2;

v1 = 4848; v2 = -3; ...

} }

(3)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 510

Ausnahmebehandlung

Wie in 3.4.1., Folie 235, bereits angesprochen, kann die Auswertung eines Ausdrucks bzw.

die Ausführung einer Anweisung:

- normal terminieren

- in eine Ausnahmesituation kommen und abrupt terminieren

- nicht terminieren

Es gibt drei Arten von Ausnahmesituationen:

1. Vom Programmierer schwer zu kontrollierende und zu beseitigende Situtationen (Speichermangel) 2. Programmierfehler (Nulldereferenzierung, Verlet-

zung von Indexgrenzen

3. Zeitweise nicht verfügbare Ressourcen, anwen- dungsspezifische Ausnahmen, die behbebbar sind.

Ausnahmesituationen werden in Programmier- sprachen unterschiedlich behandelt:

- Programmabbruch (engl. abortion) - Ausnahmebehandlung

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 511

Auslösen von Ausnahmen:

Das Auslösen einer Ausnahme kann

- sprachdefiniert (z.B. NullPointer, IndexOutOfBounds) - oder durch eine Anweisung spezifiziert sein.

In Java gibt es zum Auslösen von Ausnahmen die throw-Anweisung.

Syntax:

Die throw-Anweisung hat die Form:

<Ausdruck>;

wobei der Ausdruck ein Ausnahmeobjekt als Ergebnis liefern muss. (Die throw-Anweisung entspricht dem raise-Ausdruck in ML.) Java bietet Sprachmittel für die Ausnahme- behandlung (engl. exception handling).

Dabei spielen drei Fragen eine Rolle:

1. Wann/wie werden Ausnahmen ausgelöst?

2. Wie kann man sie abfangen?

3. Wie kann man neue Ausnahmetypen deklarieren?

void myMethod (String[] sfeld) {

try {

myPrint( sfeld[0] );

myPrint( sfeld[1] );

} catch (NullPointerException e) { myPrintln("sfeld is null");

} catch (IndexOutOfBoundsException e){

myPrintln("sfeld too small");

} }

Abfangen von Ausnahmen:

Die try-catch-Anweisung dient dem Abfangen und Behandeln von Ausnahmen (entspricht dem handle-Ausdruck in ML):

Semantik:

Werte den Ausdruck aus.

Löst die Auswertung eine Ausnahme aus, ist dies die Ausnahme, die von der Anweisung ausgelöst wird.

Andernfalls löse die Ausnahme aus, die das Ergebnis des Ausdrucks ist.

Bemerkung:

Java verlangt die Deklaration derjenigen Ausnahme- typen in der Signatur einer Methoden m, die nicht von m abgefangen werden (Genaueres in 5.3).

Beispiel:

int m( int i ) throws SomeException { if( i<0 ){

throw new SomeException();

} ...

}

Die Deklaration von Exception-Klassen behandeln wir in Abschnitt 5.3.

Benutzerdefinierte Ausnahmetypen:

Tritt eine Ausnahme vom Typ A im try-Block auf, wird ein A-Objekt X erzeugt.

Ist der Typ A in der Liste der catch-Klauseln aufgeführt, - wird die Ausnahme gefangen,

- X an den Bezeichner der entsprechenden catch- Klausel gebunden und

- diese catch-Klausel ausgeführt (Verfeinerung in 5.3).

(4)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 514

Beispiel: (Ausnahmebehandlung)

class Try {

public static void main( String[] argf ){

long maxint = 2147483647L;

try{

int m, n, ergebnis = 0 ;

m = Integer.parseInt( argf[0] );

n = Integer.parseInt( argf[1] );

long aux = (long)m + (long)n;

if( aux > maxint ) throw new Ueberlauf();

ergebnis = (int)aux ;

} catch ( IndexOutOfBoundsException e ) { System.out.println("Falsche Argumente");

} catch ( NumberFormatException e ) { System.out.println(

"Element in argf keine int-Konstante");

} catch ( Ueberlauf e ) {

System.out.println("Ueberlauf");

} }

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 515

Klassen bilden das zentrale Sprachkonstrukt von Java.

Dementsprechend stehen beim Programmentwurf zwei Fragen im Mittelpunkt:

• Welche existierenden Klassen können für den Programmentwurf herangezogen werden?

• Welche Klassen müssen neu entworfen werden?

Zur Diskussion dieser Aspekte betrachten wir ein kleines Beispiel.

Aufgabenstellung:

Ein rudimentäres Browser-Programm soll realisiert werden, mit dem einfache W3Seiten bei einem Server geholt und in einem Fenster angezeigt werden können.

Wir gehen davon aus, dass die folgenden Klassen existieren:

- W3Seite: Implementiert W3Seiten.

- W3Server: Implementiert W3Server bzw. ihre Schnittstelle.

- Textfenster: Kann W3-Seiten anzeigen.

5.2.4 Anwenden und Entwerfen von Klassen

W3Server Browser

Textfenster 1..

1

Klassendiagramm zur Lösung der Aufgabenstellung:

*

ablegen(String,W3Seite) W3Seite holen(String)

anzeigen(...)

* *

W3Seite String getTitel() String getInhalt()

*

1

Schnittstellen der gegebenen Klassen:

class W3Server { W3Server() { ... }

void ablegenSeite( String adr, W3Seite s ){

...

}

W3Seite holenSeite( String adr ) { ... } }

class TextFenster ... { ...

TextFenster() { ... }

void anzeigen(String tzeile,String text){

...

} laden(...)

aktSeite: W3Seite

/**

* Objekte repräsentieren triviale

* Web-Seiten mit Titelzeile und Inhalt

*/

class W3Seite { String titel;

String inhalt;

W3Seite ( String t, String i ) { titel = t;

this.inhalt = i;

}

String getTitel() { return this.titel;

}

String getInhalt() { return inhalt;

} }

Die vollständige Klasse W3Seiten:

(5)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 518

class Browser {

W3Server meinServer;

TextFenster oberfl;

W3Seite aktSeite; // aktuelle Seite Browser( W3Server server ){

meinServer = server;

oberfl = new TextFenster();

laden( new W3Seite("Startseite",

"NetzSurfer: Keiner ist kleiner") );

interaktiveSteuerung();

}

void laden( W3Seite s ){

aktSeite = s;

oberfl.anzeigen( aktSeite.getTitel(), aktSeite.getInhalt());

}

void interaktiveSteuerung() { ... } }

Wichtige Implementierungsteile einer rudimentären Browser-Klasse:

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 519

In Java ist es erlaubt, innerhalb einer Klasse mehrere Methoden mit dem gleichen Namen zu deklarieren, d.h. es gibt zwei Bindungen mit gleichem Namen.

Eine derartige Mehrfachverwendung nennt man Überladen eines Namens. Methoden mit gleichen Namen müssen sich in der Anzahl oder in den Typen der Parameter unterscheiden.

Durch die unterschiedliche Signatur kann der Übersetzer die Überladung auflösen, d.h. für jede Aufrufstelle ermitteln, welche von den Methoden gleichen Namens an der Aufrufstelle gemeint ist.

Entsprechend dem Überladen von Methodennamen erlaubt Java auch das Überladen bei Konstruktoren.

5.2.5 Spracherweiterungen: Überladen, Klassenvariablen und -methoden Überladen

Beispiel

:

(Überladen)

Die Java-Bibliothek bietet viele Beispiele für Überladung. Wir betrachten die Klasse String (hier nur unvollständig wiedergegeben):

class String {

/** The value is used for character storage */

char[] value;

/** The offset is the first index of the used storage*/

int offset;

/** The count is the number of characters in the ... */

int count;

String() { value = new char[0]; } String( String value ) { ... } String( char[] value ) {

this.count = value.length;

this.value = new char[count];

System.arraycopy(value,0,this.value,0,count);

} ...

int indexOf(int ch) { return indexOf(ch, 0);}

int indexOf(int ch, int fromIndex) { ... } int indexOf(String str) { ...}

int indexOf(String str, int fromIndex) {...}

int length() { return count; } char charAt(int index) {

if ((index < 0) || (index >= count)) { throw

new StringIndexOutOfBoundsException(index);

}

return value[index + offset];

} }

Klassenattribute und Klassenmethoden

Die Deklaration eines Klassenattributs liefert eine klassenlokale Variable. Syntax:

static <Typausdruck> <Attributname> ; Klassenattribute/-variablen werden häufig auch als statische Attribute/Variablen bezeichnet.

Die Variable kann innerhalb der Klasse mit dem Attributnamen, außerhalb mittels

<Klassenname> . <Attributname>

angesprochen werden. Die Lebensdauer der Variablen entspricht der Lebensdauer der Klasse.

Bemerkung:

Klassenvariablen verhalten sich ähnlich wie globale Variablen in der prozeduralen Programmierung.

(6)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 522

Beispiel: (Klassenattribut)

class InstanceCount {

static int instCount = 0;

InstanceCount(){

instCount++;

...

} ...

}

Die Deklaration einer Klassenmethode entspricht der Deklaration einer Prozedur. Klassenmethoden besitzen keinen impliziten Parameter. Sie können nur auf Klassenattribute, Parameter und lokale Variable zugreifen. Syntax:

static <Methodendeklaration>

Klassenmethoden werden häufig auch als statische Methoden bezeichnet.

Klassenmethoden werden mit folgender Syntax aufgerufen:

<Klassenname> . <Methodenname> ( ... )

Innerhalb der Klasse kann der Klassenname entfallen.

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 523

Beispiel: (Klassen-, statische Methoden)

Deklaration:

class String { ...

static String valueOf( long l ) { ... } static String valueOf( float f ) { ... } ...

}

Anwendung/Aufruf:

String.valueOf( (float)(7./9.) ) liefert die Zeichenreihe: "0.7777778"

Bemerkung:

In Kapitel 4 wurden Klassenmethoden zur prozeduralen Programmierung in Java genutzt.

class System {

final static InputStream in = ...;

final static PrintStream out = ...;

static void exit(int status) { ... } static native void arraycopy(

Object src,int src_position,

Object dst,int dst_position, int length);

}

Die Klasse PrintStream besitzt Methoden print und println:

System.out.print("Das klaert die Syntax");

System.out.println(" von Printaufrufen");

Beispiele: (Klassenattribute u. -methoden)

1. Charakteristische Beispiele für Klassenattribute und -methoden liefert die KlasseSystem, die eine Schnitt- stelle von Programmen zur Umgebung bereitstellt:

2. Unsere Klasse InputOutput liefert auch schöne Beispiele für statische Methoden und Überladung:

public class InputOutput {

public static int readInt(){...}

public static String readString(){...}

public static char readChar(){...}

public static void print(int i){

System.out.print(i);

}

public static void println(int i){

System.out.println(i);

}

public static void print(char c){

System.out.print(c);

}

public static void println(char c){

System.out.println(c);

}

public static void print(String s){

System.out.print(s);

}

public static void println(String s){

System.out.println(s);

} }

(7)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 526

5.2.6 Zusammenwirken der Spracherweiterungen

Das Zusammenwirken der eingeführten Sprach- elemente erlaubt bereits, recht komplexe Programme zu schreiben.

Folgendes Programmbeispiel mischt prozedurale und objektorientierte Sprachelemente. Es dient zum Studium des Zusammenwirkens der Spracherweiterungen.

Beispiel: (Zusammenwirken von Sprachel.)

Wir erweitern das Browserbeispiel von 5.2.4:

- Unterstützung mehrerer Browserfenster - Interaktive Steuerung über die Konsole

class Konsole {

static String readString() { ... }

static void writeString( String s ) {...}

}

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 527

Entwurf der Implementierung:

- Die gemeinsamen Teile aller Browserfenster werden durch Klassenattribute und –methoden realisiert.

- Es gibt zwei Konstruktoren: Einer startet das erste Browserobjekt; der andere weitere Browserobjekte.

- Die gemeinsamen Teile der Konstruktoren werden von der Methodeinitialisieren erledigt.

- Die interaktive Steuerung von der Konsole wird durch eine statische Methode implementiert.

- Zur einfacheren Handhabung steht eine

Klassenmethodestart zur Verfügung, die den W3Server als Argument bekommt:

...

Browser.start( testServer );

...

- Die Browserfenster werden in einem Feld auf Klassenebene verwaltet.

class Browser { TextFenster oberfl;

W3Seite aktSeite;

static W3Server meinServer;

static final int MAX_ANZAHL = 4;

static Browser[] gestarteteBrowser =

new Browser[MAX_ANZAHL];

static int naechsterFreierIndex = 0;

static int aktBrowserIndex;

static W3Seite startseite = new W3Seite("Startseite",

"NetzSurfer: Keiner ist kleiner");

// Konstruktor für ersten Browsers Browser( W3Server server ) {

if( naechsterFreierIndex != 0 ) {

System.out.println("Browser gestartet");

} else {

meinServer = server;

initialisieren();

} }

// Konstruktor für weiterere Browserfenster Browser() {

if( naechsterFreierIndex == MAX_ANZAHL ) { System.out.print("Maximale Anzahl ");

System.out.println(" Browser erreicht");

} else {

initialisieren();

} }

static void start( W3Server server ) { new Browser(server);

Browser.interaktiveSteuerung();

}

void initialisieren() {

oberfl = new TextFenster();

gestarteteBrowser[ naechsterFreierIndex ]

= this;

aktBrowserIndex = naechsterFreierIndex;

naechsterFreierIndex++ ; laden( startseite );

}

void laden( W3Seite s ){

aktSeite = s;

oberfl.anzeigen(aktSeite.getTitel(), aktSeite.getInhalt());

}

(8)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 530 static void interaktiveSteuerung() {

char steuerzeichen = '0';

do {

Konsole.writeString("Steuerzeichen [lnwe]: ");

try {

String eingabe = Konsole.readString();

if( eingabe.equals("") ) steuerzeichen = '0';

else

steuerzeichen = eingabe.charAt(0);

} catch( Exception e ) { System.exit( 0 );

}

switch( steuerzeichen ){

case 'l':

String seitenadr;

Konsole.writeString("Seitenadresse: ");

seitenadr = Konsole.readString();

gestarteteBrowser[aktBrowserIndex] .

laden( meinServer.holenSeite( seitenadr ) );

break;

case 'n': new Browser(); break;

case 'w':

aktBrowserIndex =

(aktBrowserIndex+1) % naechsterFreierIndex;

break;

case 'e':

System.exit( 0 );

default:

Konsole.writeString("falsche Eingabe\n");

}

} while( true );

} }

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 531

5.2.7 Rekursive Klassen

Definition: (rekursive Klassendeklarationen)

Eine Klassendeklaration K heißt direkt rekursiv, wenn Attribute von K den Typ K haben.

Eine Menge von Klassendeklarationen heißt verschränkt rekursiv oder indirekt rekursiv (engl. mutually recursive), wenn die Deklarationen gegenseitig voneinander abhängen.

Eine Klassendeklaration heißt rekursiv, wenn sie direkt rekursiv ist oder Element einer Menge verschränkt rekursiver Klassendeklarationen ist.

Bemerkung:

• Wir identifizieren Klassen mit ihren Deklarationen.

• Wichtige Anwendung rekursiver Klassen ist die Implementierung von Listen-, Baum- und Graph- strukturen.

Im Folgenden betrachten wir rekursive Klassen für Listen. Dabei variieren wir die Programmierstile und die bereitgestellten Schnittstellen.

Implementierung von Listen

Beispiel: (Einfachverkettete Listen)

class ProcList { int head;

ProcList tail;

}

Bei einfachverketteten Listen gibt es für jedes Listenelement ein Objekt mit zwei Instanzvariablen:

- zum Speichern des Elements

- zum Speichern der Referenz auf den Rest der Liste.

: ProcList head: 6

tail:

: ProcList : ProcList head: -3

tail:

head: 84 tail:

Prozedurale Datenstrukturen:

In der prozeduralen Programmierung sind Datentypen und Prozeduren zunächst getrennt (Zusammenfassung erst auf Modulebene).

class ProcListMain {

static boolean sortiert(ProcList l){

if( l == null || l.tail == null ) { return true;

} else if( l.head <= l.tail.head ){

return sortiert( l.tail );

} else {

return false;

} }

public static void main( String[] argf ){

ProcList l1 = new ProcList();

ProcList l2 = new ProcList();

ProcList l3 = new ProcList();

l1.head = 1;

l2.head = 2;

l3.head = 3;

l1.tail = l2;

l2.tail = l3;

l3.tail = null;

System.out.println( sortiert(l1) );

l3.head = 0;

System.out.println( sortiert(l1) );

} }

(9)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 534

Diskussion:

Die prozedurale Fassung erlaubt es jedem, der eine Referenz auf ein Listenknoten hat, das Objektgeflecht unkontrolliert zu verändern.

Zum Beispiel könnte man Listen in ein zyklisches Geflecht verändern und damit Invarianten verletzen.

Funktionale Datenstrukturen:

Unterbindet man den beliebigen Zugriff auf die Attribute und bietet nur Methoden an, um Listen auf- und abzubauen, kann man ein

Verhalten wie in der funktionalen Programmierung erreichen.

Das Mehr an Garantien wird durch weniger Flexibilität bezahlt. Insbesondere ist das direkte Einfügen und Modifizieren in der „Mitte“ einer Datenstruktur nicht mehr möglich.

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 535

Beispiel: (Zugriff nur über Methoden)

class FunctionalList { private int head;

private FunctionalList tail;

static FunctionalList empty() { return new FunctionalList();

}

boolean isempty(){

return tail == null;

}

int head(){

if( isempty() ){

throw new NoSuchElementException();

}

return head;

}

FunctionalList tail(){

if( isempty() ){

throw new NoSuchElementException();

}

return tail;

}

FunctionalList cons( int i ) {

FunctionalList aux = new FunctionalList();

aux.head = i;

aux.tail = this;

return aux;

} }

class FunctionalListMain {

static boolean sortiert( FunctionalList l ){

if( l.isempty() || l.tail().isempty() ) { return true;

} else if( l.head() <= l.tail().head() ){

return sortiert( l.tail() );

} else {

return false;

} }

public static void main( String[] argf ){

FunctionalList le, l1, l2, l3;

le = FunctionalList.empty();

l3 = le.cons(3);

l2 = l3.cons(2);

l1 = l2.cons(1);

System.out.println( sortiert(l1) );

l1 = l3.cons(4);

System.out.println( sortiert(l1) );

} }

Objektorientierte Listen:

Aus objektorientierter Sicht ist eine Liste ein Behälter, in den man etwas hineintun und aus dem man etwas herausnehmen kann:

Beispiel: (Liste als Behälter)

class SLinkedList {

// Liefert das erste Element der Liste, // ohne diese Liste zu veraendern int getFirst(){ ... }

// Fügt vorne ein neues Element an diese // Liste an

void addFirst( int n ) { ... }

// Löscht das erste Element dieser Liste // und liefert es als Ergebnis

int removeFirst() { ... }

// Liefert die Elementanzahl dieser Liste int size() { ... }

}

class SLinkedListMain {

public static void main( String[] argf ){

SLinkedList l = new SLinkedList();

l.addFirst(3);

l.addFirst(2);

l.addFirst(1);

System.out.println( l.removeFirst() );

System.out.println( l.size() );

System.out.println( l.removeFirst() );

} }

Zunächst die Schnittstelle und Anwendung der Klasse:

(10)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 538

Und nun Teile der Implementierung vonSLinkedList:

import java.util.NoSuchElementException;

class SEntry { int head;

SEntry tail;

}

class SLinkedList {

private SEntry entries = null;

private int size = 0;

int getFirst(){

if( size == 0 ){

throw new NoSuchElementException();

}

return entries.head;

}

void addFirst( int n ) { size++;

SEntry auxe = new SEntry();

auxe.head = n;

auxe.tail = entries;

entries = auxe;

} ...

}

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 539

: Entry element:

next:

:LinkedList header:

size: 3

previous:

: Entry element:

next:

previous:

: Entry element:

next:

previous:

: Entry element:

next:

previous:

Problem bei der erläuterten Implementierung:

Rekursives oder iteratives Durchlaufen durch die Liste ist nicht möglich (Abhilfe: s.u.).

Andere Formen von Listenimplementierungen speichern die Elemente in Feldern oder nutzen eine doppelte Verkettung der Eintragsknoten:

... ... ...

Implementierung von Bäumen

Binäre Bäume sind wichtige Datenstrukturen, z.B.

zur Darstellung von Mengen.

class BinTree { private int elem;

private BinTree left, right;

BinTree( int e ) { elem = e;

}

void sorted_insert( int e ) { if( e < elem ) {

if( left == null ) { left = new BinTree(e);

} else {

left.sorted_insert(e);

}

} else if( elem < e ) { if( right == null ) {

right = new BinTree(e);

} else {

right.sorted_insert(e);

} } }

// weiter auf nächster Folie

Beispiel: (Bäume)

boolean contains( int e ) {

if( e < elem && left != null ) { return left.contains(e);

} else if( elem > e && right != null ) { return right.contains(e);

} else {

return e==elem;

} }

void printTree() {

if( left != null ) left.printTree();

System.out.println(elem);

if( right != null ) right.printTree();

} }

class BinTreeMain {

public static void main( String[] argf ){

BinTree bt = new BinTree(12);

bt.sorted_insert(3);

bt.sorted_insert(12);

bt.sorted_insert(11);

bt.sorted_insert(12343);

bt.sorted_insert(-2343);

bt.sorted_insert(233);

bt.printTree();

} }

(11)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 542

Iteratoren

Iteratoren erlauben es, schrittweise über Behälter- Datenstrukturen zu laufen, so dass alle Elemente der Reihe nach besucht werden. Im Zusammenhang mit Kapselung (s.u.) sind sie unverzichtbar.

Beispiel: (Iteratoren)

Wir reichern die Klasse SLinkedList mit Iteratoren an und zeigen deren Anwendung.

class Iterator { SEntry current;

Iterator( SEntry se ) { current = se;

}

boolean hasNext() { return current!=null;

}

int next() {

if( current==null ){

throw new NoSuchElementException();

}

int res = current.head;

current = current.tail;

return res;

} }

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 543

class SLinkedList {

private SEntry entries = null;

...

Iterator iterator() {

return new Iterator( entries );

} }

class SLinkedListMain {

public static void main( String[] argf ){

SLinkedList l = new SLinkedList();

l.addFirst(3);

l.addFirst(2);

l.addFirst(4);

Iterator iter = l.iterator();

while( iter.hasNext() ) {

System.out.println( iter.next() );

} } }

Bemerkung:

Der Iterator muss Zugriff auf die interne Repräsentation der Datenstruktur haben, über die er iteriert.

5.2.8 Typsystem von Java und parametrische Typen

Werte in Java sind

- die Elemente der elementaren Datentypen, - Referenzen auf Objekte,

- der Wert null.

Jeder Wert in Java gehört zu mindestens einem Typ.

Vordefinierte und benutzerdeklarierte Typen sind - die vordefinierten elementaren Typen,

- die durch Klassen deklarierten Typen,

- die durch Schnittstellen deklarierten Typen (s.u.).

Implizit deklariert sind die Feldtypen zu den Klassen- und Schnittstellentypen (Typkonstruktor „[]“ ).

Feld-, Klassen- und Schnittstellentypen fasst man unter dem Namen Referenztypen zusammen.

(null gehört zu allen Referenztypen.)

Klassen- und Schnittstellentypen beschreiben, welche Nachrichten ihre Objekte verstehen bzw. welche Methoden sie besitzen.

Typsystem von Java

Bemerkung:

In typisierten objektorientierten Sprachen können Werte zu mehreren Typen gehören (Subtypen).

Parametrische Typen

Auch objektorientierte Sprachen unterstützen parametrische Typsysteme wie in ML. Für Java ist eine derartige Unterstützung ab Version 1.5 verfügbar.

Wir betrachten eine parametrische Fassung der Klasse SLinkedList:

Beispiel: (Parametrische Typen)

class SLinkedList<A> { A getFirst(){ ... }

void addFirst( A n ) { ... } A removeFirst() { ... } int size() { ... } }

(12)

17.01.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 546

class Test {

public static void main( String[] argf ){

SLinkedList<String> l

= new SLinkedList<String>();

l.addFirst("Die Ersten werden");

l.addFirst("die Letzten sein");

int i = l.getFirst().indexOf("sein");

// liefert 13 SLinkedList<W3Seite> l

= new SLinkedList<W3Seite>();

l.addFirst(new W3Seite("Titel","Inhalt");

l.addFirst(new W3Seite("Title","Content");

int i = l.getFirst().indexOf("sein");

// liefert Übersetzungsfehler }

}

In Java 5 ist die Instanzierung der Typ- Parameter nur durch Referenztypen gestattet:

Referenzen

ÄHNLICHE DOKUMENTE

in zeitlicher Abfolge berichtet wurde, in vier oft voneinander abweichenden Schriften mehr oder weniger biographisch-berichtenden Charakters ihren Niederschlag fand.

Bei der topo- graphischen Wichtigkeit von Carlsbad und seiner Umgebung, bei dem bedeutenden Interesse, welches in grossen Kreisen für diesen Distrikt von Böhmen gehegt wird, schien

Klafter :610'6 Meter, welches Signal jedoch nicht mehr vorhanden ist.

6155 Hilla Berg, waldige Bergkuppe östlich von Carls- bad,

„Meeresschutzgebiete können innerhalb ihrer Grenzen große Vorteile für die biologische Vielfalt bringen, zum Beispiel durch den Schutz von Lebensräumen, Artenbeständen,

It occurs through mechanisms as diverse as (inter)national fisheries gov- ernance and trade and investment policies, designated terrestrial, coastal and marine ‘no-take’

For example, the current and potential exploitation of sensitive deep-sea habitats for resource extraction (e.g. through mining or fishing) highlights the need for better

Um der fragmentierten Debatte um die Wirksamkeit von EZ gerecht zu werden, schlagen wir einen integrativen Ansatz vor, der vier weitgehend getrennten politische und