DIE WICHTIGSTEN STICHWORTE UND FAKTEN ZUM THEMA
"OBJEKT-ORIENTIERTE PROGRAMMIERSPRACHEN"
UND OO IN JAVA
1. OBJEKTE
1.1 Objekte werden durch ZUSTAND und VERHALTEN beschrieben Die
Zustandskomponenten heissen ATTRIBUTE, das Verhalten wird durch
Methoden (oder synonym: Operationen) spezifiziert. Der
Internzustand sowie bestimmte Operationen, die nur innerhalb
Objektes benötigt werden, können nach dem GEHEIMNISPRINZIP
(Information Hiding) vor externem Zugriff geschützt werden. Die
möglichen Verhaltensweisen eines Objektes werden durch das
Versenden von BOTSCHAFTEN an das Objekt ausgelöst. Während bei der
nicht objekt-orientierten imperativen Programmierung Daten an eine
Funktion zur Verarbeitung übergeben werden, kapselt das Objekt
Daten und auf ihnen anwendbare Funktionen; die Verarbeitung der
Daten wird durch Senden einer Botschaft an das Objekt
ausgelöst. Die Parameter der Botschaft dürfen wiederum Objekte
sein, d.h. intuitiv gesprochen, man kann mit den Eingangsdaten
einer Botschaft auch gleich die Methoden zur Verarbeitung dieser
Eingangsdaten mitsenden.
[Literatur: Balzert, Abschnitt 2.5]
1.2 Methoden (synonym: Operationen) lassen sich in Prozeduren und Funktionen
gliedern. Prozeduren sind void-Methoden. Funktionen sind Methoden, die
einen Rückgabewert erzeugen.
Methoden haben eine SIGNATUR, diese ist definiert durch
- Methodenname
- Anzahl und Typ und Reihenfolge der Parameter
[Bemerkung: Der Rückgabewert einer Funktion ist NICHT Bestandteil
der Signatur].
Methoden können ÜBERLADEN (engl. OVERLOADING) werden:
Hierunter versteht man
die mehrfache Deklaration der Methode mit unterschiedlichen Signaturen.
[Literatur: Balzert, Abschnitt 2.11 und pp. 258-259]
In Java:
- Die Zustandsspezifikation erfolgt durch Feld-Variablen (auch
Attribute genannt).
- Es dürfen nur Methoden, aber keine Operatoren (z.B. +,-,==)
überladen werden. Das Überladen von Operatoren ist
beispielsweise in C++ erlaubt.
1.3 Konstruktoren unterscheiden sich von Methoden, weil sie nur der Erzeugung
einer Objektinstanz dienen. Sie haben daher keinen spezifizierten
Rückgabewert (auch nicht void!),
denn dieser ist immer - durch den new-Operator weiter
vermittelt - der Referenztyp des erzeugten Objektes.
Speziell in Java:
- Wird ein Objekt durch den Garbage Collector deallokiert,
kann eine Finalization-Methode aufgerufen werden.
(VORSICHT: Zeitpunkt der Garbage Collection und des
Aufrufs der Finalisierungsmethode muss explizit
über Methoden der Laufzeitumgebung gesteuert werden.
Andernfalls hat mein keine Sicherheit, wann das
Objekt tatsaechlich "finalisiert" und zerstört wird.)
Eine spezielle Ausprägung der Konstruktoren sind sog. Copy
Konstruktoren. Diese nehmen ein Objektreferenz auf das zu kopierende Objekt
als Eingabeparameter und erzeugen im neuen Objekt eine Kopie des referenzierten
Objektes. Bei Copy-Kontruktoren ist zu unterscheiden, wie mit
Referenzattributen des zu kopierenden Objektes umgegangen wird:
- Shallow copy: Es werden nur die Referenzen kopiert, nicht die
referenzierten Objekte selbst.
-
Deep copy: Es werden auch die referenzierten Objekte selbst kopiert,
ggf. auch rekursiv, wenn diese wieder selbst Objekte in ihren Attributen
referenzieren.
2. KLASSEN
Klassen spezifizieren die Gemeinsamkeiten einer Menge von
Objekten mit denselben Eigenschaften (Attributen) und
demselben Verhalten (Methoden) und denselben Beziehungen
(d.h. Vererbungsstrukturen und Assoziationen, siehe Vererbung
weiter unten).
ABSTRAKTE KLASSEN definieren nicht alle erforderlichen
Methoden für ihre Objekte, sondern nur Methodenschnittstellen.
Sind Klassen nicht abstrakt, bieten sie insbesondere
Methoden zur Objekterzeugung an (Konstruktoren). Die Klasse
dient dann als "Objektfabrik" OBJECT FACTORY. Durch die Klasse
erzeugte Objekte heissen INSTANZEN der Klasse.
In Java:
- Schlüsselworte 'abstract', 'public', 'final'
('final' bedeutet, es dürfen keine Unterklassen erzeugt werden).
- Java-Klassen sind Typen im Sinne der
Objekt-Orientierung,
decken aber nicht das gesamte OO-Typkonzept ab, wie es
üblicherweise verwendet wird, siehe Punkt 3.
Die Attribute und Methoden einer Klasse werden gmeinsam als
MITGLIEDER (engl. members) der Klasse bezeichnet.
Klassen können VERERBT werden: Eine Klasse (dann Unterklasse oder
abgeleitete Klasse genannt)
kann die Attribute und Methoden einer oder mehrerer Oberklassen
übernehmen und dann eigene Attribute und Methoden hinzudefinieren.
Ein Objekt O der Unterklasse K2 ist damit auch ein Mitglied einer jeden
Oberklasse K1: Bei Methodenaufrufen, die Parameter vom Typ K1 verlangen,
darf also O als gültiger Wert verwendet werden. O besitzt als
Instanz von K2 spezielle Eigenschaften, die in K1 nicht enthalten
sind. Daher wird K2 als SPEZIALISIERUNG von K1 bezeichnet; umgekehrt
ist K1 eine GENERALISIERUNG von K2.
Dabei dürfen in Oberklassen vorhandene Methoden von der Unterklasse
- REDEFINIERT (engl. REDEFINE) werden (die Signatur bleibt
gleich) oder
- ÜBERSCHRIEBEN (engl. OVERRIDE) werden
(der Methodenname bleibt gleich, die Signatur ändert
sich). [VORSICHT: Diese Begriffsdefinitionen sind nach
Balzert eingeführt. Gosling benutzt 'Overriding' für
Redefinitionen und 'Overloading' für das Überschreiben,
unterscheidet als nicht zwischen Überladen und
Überschreiben. Wichtig ist einfach, die drei Möglichkeiten
(1) Einführung einer zusätzliche Methode gleichen Namens
mit neuer Signatur in derselben Klasse, (2) Einführung
einer zusätzlichen Methode gleichen Namens und gleicher
Signatur in der Unterklasse, (3) Einführung
einer zusätzlichen Methode gleichen Namens und anderer
Signatur in der Unterklasse, zu verstehen und unterscheiden
zu können.]
Damit können Objektinstanzen unterschiedlicher in Vererbungsbeziehung
stehender Klassen Methoden mit
identischer Signatur oder gleichen Namens besitzen, die unterschiedliches
Verhalten implementieren. Diese Eigenschaft objekt-orientierter
Programmiersprachen wird POLYMORPHISMUS genannt. Wird ein
Objekt O als Parameter einer Methode vom Typ K1 eingegeben, ist damit nicht
immer zur Übersetzungszeit bereis bekannt, ob zur Laufzeit
auch ein Objekt der Unterklasse K2 als Parameter verwendet
wird. Infolgedessen ist erst zur Laufzeit bekannt, ob beim
Aufruf O.m() die Methode m() der Klasse K1 oder die in K2 redefinierte
Methode m() zur Ausführung kommt. Dies muss von der Laufzeitumgebung
ermittelt werden (DYNAMSCHES BINDEN).
[Literatur: Reinhard Schiedermeier: Programmieren in Java. Pearson
2005, pp. 232]
In Java:
- Syntax ist
class <Classname> extends <Classname> {
...
}
- Es darf nur von einer Klasse geerbt werden (EINFACHVERERBUNG)
- hierdurch ensteht eine Klassenhierarchie mit Baumstruktur.
Die Wurzelklasse dieser Baumstruktur ist 'Object'. (Im
Gegensatz zu Java lässt C++ beispielsweise MEHRFACHVERERBUNG zu)
- Eine Klasse darf beliebig viele Interfaces implementieren
(die interface Deklaration wird weiter unten erläutert).
- Die Konstruktoren der Oberklasse koennen explizit mit
super()
bzw. super(<Parameterliste>)
referenziert
werden. Die Mitglieder (Attribute und Methoden) der Oberklasse
können durch Vorstellen von <Name der
Oberklasse>.
oder einfach von super.
referenziert werden.
- Die Konstruktoren der Unterklasse koennen explizit mit
this(<Parameterliste>)
referenziert
werden.Die Mitglieder (Attribute und Methoden) der Unterklasse
können explizit durch Vorstellen von <Name der
Unterklasse>.
oder einfach von this.
referenziert werden. Insbesondre kann sich jede
Instanz der Unterklasse durch this
selbst
referenzieren. Dies ist beispielsweise erforderlich, wenn eine
Methode eine Referenz auf ein Objekt des Klassentyps
zurückgibt und diese Referenz auf das eigene Objekt zeigen soll.
- Attribute können bei der Vererbung nicht überschrieben,
sondern nur VERSTECKT (engl. HIDING)
werden: Definiert die Unterklasse ein
gleichnamiges Attribut der Oberklasse, so ist das Attribut der
Oberklasse weiterhin durch
super.<Attributname>
referenzierbar.
Statische (d.h. Klassenattribute) können alternativ mit
<Name der
Oberklasse>.<Attributname>
referenziert werden.
- Feld-Variablen können Klassen- oder
Objektattribute sein: Alle Instanzen einer Klasse "sehen"
dieselben Klassenattribute und besitzen eigene
lokale Objektattribute. Klassenattribute werden durch
Schlüsselwort 'static' gekennzeichnet.
- Schlüsselwort 'public' macht Attribute und Operationen
für alle verfügbar
- Schlüsselwort 'private' schützt Attribute und Operationen
vor externem Zugriff
- Schlüsselwort 'protected' macht Attribute und Operationen
für Unterklassen verfügbar, welche die Methoden und
Attribute der Oberklasse erben.
- Das Versenden von Botschaften an ein Objekt wird durch
Methodenaufrufe der syntaktischen Form
<objektname>.<Methodenname mit Aktualparametern>
vollzogen.
3. TYPEN
Während Klassen typischerweise Zustand und Verhalten zumindest
teilweise explizit definieren, benötigt man auch ein Konzept, mit
dem man Leistungen gewünschter (später explizit
auszuprägender) Objekte IMPLIZIT spezifizieren kann, d.h.,
ohne Zustandskomponenten und Methodenrümpfe explizit zu benennen.
Allgemein besteht eine implizite Spezifikation aus
- einer Schnittstellenspezifikation bestehend aus
Signatur und ggf. Typ des Rückgabewertes
- optional: einem logischen Prädikat (häufig auch AXIOM
genannt), welches das Sollverhalten eines
der impliziten Spezifikation genügenden Objektes
beschreibt, ohne auf Internzustände und Algorithmen
Bezug zu nehmen, d.h.: das Axiom bezieht sich nur
auf Komponenten, die in der Schnittstellenspezifikation
sichtbar sind, beispielsweise
- Methodennamen,
- Parameternamen,
- Rückgabewerte
Eine besonders h&aauml;ufig verwendete Spezifikationsweise wür
Axiome sind die Vor- und Nachbedingungen (Pre-/Postconditions).
Formale Sprachen (z.B. VDM, Z, CSP und
verschiedene algebraische Spezifikationsformalismen) bieten in der
Regel die Möglichkeit zur impliziten Spezifikation mit
Schnittstellenspezifikation und Axiomen. Dagegen bestehen
implizite Spezifikation bei Programmiersprachen in der Regel allein
aus der Schnittstellenspezifikation; die Definition logischer
Prädikate wird nicht als Sprachbestandteil angesehen. Beispiele
für implizite Spezifikation bei Programmiersprachen:
- C: Function prototypes in C-Header Files, wie z.B.
extern int f(int i,float ff,int j);
- C++: Klassendefinitionen in C++-Header Files,
deren Methoden sämtlich als 'virtual'
definiert sind und die keine Zustandsvariablen einführen.
In Java:
- Werden Methodenspezifikationen mit Hilfe von Doxygen erstellt, können
Vor- und Nachbedingungen mit den Schlüsselworten
@pre, @post
beschrieben werden (geht analog bei C/C++ und wird dort vorzugsweise in die
Header Files eingetragen).
- Der Typ 'interface' dient zur impliziten Spezifikation.
- Ein Interface kann (offensichtlich) nicht direkt
instantiiert werden. Stattdessen wird ein Klassentyp
durch Hinzunahme eines Interfaces und seiner Implementierung
erweitert.
- Klassen dürfen mehrere Interfaces implementieren, Deklarationssyntax
class <Classname> implements <Interfacename> {
...
}
- Interfaces dürfen keine Zustandsvariablen definieren, wohl aber
Konstanten (z.B.
public static final int x = 5;
).
- Abstrakte Klassen können dasselbe wie Interfaces leisten, wenn
sie nur abtrakte Methoden und keine Zustandsvariablen definieren. Eine
Unterklasse darf dann aber nur diese eine abstrakte Klasse erweitern,
da Mehrfachvererbung nicht möglich ist.
Vereinfacht ausgedrückt: In Java sind Interfaces reine
Entwurfsspezifikationen, während Klassen Mischungen aus
Entwurfsspezifikation und Implementierung sind.
[Literatur: Reinhard Schiedermeier: Programmieren in Java. Pearson
2005, pp. 224]
Manche Programmiersprachen lassen das "Programmieren mit
Typen" zu, d.h. sie bieten Methoden zur Abfrage von Eigenschaften
eines Objekttyps an. In C-Code kann man beispielsweise nicht abfragen,
welchen Funktionsnamen die aktuell ausgeführte C-Function hat. Es gibt
auch keine C-Operationen, mit denen man die Namen und Signaturen der
in einer C-Bibliothek enthaltenen Funktionen abfragen kann.
Dagegen in Java:
- Das Paket java.lang enthält die Klasse
Class
, deren Objekte "Auskunftsfunktionen" über andere
Objekte enthalten. Ein Objekt obj
beliebiger Klasse enthält die Methode
getClass()
, die ein solches Auskunftsobjekt
oInfo
vom Typ
Class
zurückgibt. Auf oInfo
kann man
vordefinierte Abfragefunktionen anwenden, die Auskunft über den
Klassentyp von obj
geben. Beispielsweise gibt die
Class
-Methode getMethods()
einen Array mit
internen Identifikationscodes der zu obj
gehörigen
Methoden zurück.
- Im Java reflection package
java.lang.reflect
werden zusätzliche Auskunftsklassen
definiert: Beispielsweise bietet Klasse Method
Methoden
an, mit denen man aus oInfo
Signatur und Rückgabewert der
zu obj
gehörigen Methoden abfragen kann. Weiterhin lassen
sich diese Methoden dann aufrufen (zu Method
gehörige
Methode invoke()), ohne den Methodennamen im
Programmcode zu verwenden.
- Klassennamen existieren als vorübersetzte Literale mit Extension
.class, man kann damit auf Zugehörigkeit zu einem
bestimmten Klassentyp abfragen:
if ( obj.getClass() == String.class ) { ... }
4. GENERISCHE FUNKTIONEN UND KLASSEN
Die Eigenschaft generisch
bedeutet 'Typunabhängigkeit'. Mit diesem Begriff ist die Möglichkeit
gemeint, bei der Instantiierung eines Objektes aus einer Klasse auch
parametrisierbare Typen (in C++ Templates genannt) in den
Methodenschnittstellen
festzulegen und bei Methodenaufrufen auch anzuwendende
Verarbeitungsfunktionen als Parameter mitzugeben.
In Java:
Templates und Funktionsparameter sind nicht als explizite
Sprachkonstrukte vorhanden, aber auch nicht erforderlich, da die
baumartige Vererbungshierarchie immer Object als
allgemeinsten Verarbeitungstyp kennt.
- Anstelle eines generischen Typparameters kann in einer
Methodendeklaration immer der Typ 'Object' verwendet
werden. Müssen die auf Object definierten Methoden
(z.B.
equals()
) um weitere dort nicht bereits
eingeführte erweitert werden, lässt sich dies mit Hilfe von
abstrakten Klassen und Interfaces realisieren.
- Zur Laufzeit wird beim Methodenaufruf
als Parameter ein Objekt einer konkreteren
Klasse eingegeben; dies ist in jedem Fall typkorrekt, da die
konkretere Klasse immer eine Spezialisierung von Object ist.
- Anstelle von Funktionsparametern für die Verarbeitung der
konkreten Objekte werden die Verarbeitungsfunktionen immer als
Methoden der konkreten Klasse definiert, ggf. durch Überschreiben
der bereits auf Object definierten, in der speziellen Anwendung
aber zu konkretisierenden Methoden.
- Bei den Template-Klassen von C++ kann die
Sortenreinheit,
d.h. die ausschließliche Verwendbarkeit des Objekts mit den bei
der Instantiierung angegebenen Typen bereits zur
Übersetzungszeit erzwungen werden. In Java muss diese
Eigenschaft, falls gewünscht, durch explizite Typprüfungen zur
Laufzeit realisiert werden. Hierzu kann man den Objekttyp abfragen
und auch das Vorhandensein bestimmter für die Verarbeitung
benötigter Methoden explizit abfragen.
5. ZUSAMMENFASSUNG: CHARAKTERISTISCHE EIGENSCHAFTEN OBJEKT-ORIENTIERTER
PROGRAMMIERSPRACHEN
- Kapselung von Daten und Zugriffsfunktionen (Methoden) in Objekten
- Geheimnisprinzip (information hiding)
- Überladen von Funktionen (overloading)
- Überladen von Operatoren (geht nicht bei Java)
- Klassen als Objektspezifikationen, mit Konstruktoren zur Objekterzeugung
(object factory)
- Vererbung von Klassenspezifikationen (Attribute und Methoden)
- Überschreiben von Funktionen bei der Vererbung (overriding) - die
erbende Klasse führt Methoden der Oberklasse mit neuer Signatur ein
- Redefinition von Funktionen bei der Vererbung (re-definition) - die
erbende Klasse ersetzt Methoden der Oberklasse mit gleicher Signatur und
eigener Implementierung
- Polymorphismus: Durch Redefinition führt die Anwendung eines syntaktisch
identischen Methodenaufrufs zu unterschiedlichen Verhaltensweisen
- Dynamisches Binden: der konkrete Objekttyp - und damit die konkreten
darauf anzuwendenden Methoden - werden zur Laufzeit bestimmt, weil sie
u.U. zur Übersetzungszeit gar nicht feststellbar sind
- Generische Klassenspezifikationen (Template Klassen)
Jan Peleska
- TZI - Bremen Institute of Safe Systems BISS /
<
jp@informatik.uni-bremen.de>
/ 29 MAY 2001