Next: Die final-Kennzeichnung
Up: Vererbung
Previous: Dynamische Bindung
Zwei Methoden einer Klassen heißen überladen, wenn sie den gleichen
Namen aber unterschiedliche Signatur haben.
Es spielt keine Rolle,
- ob die Methoden in der Klasse deklariert oder geerbt sind.
- wie der Ergebnistyp der Methoden aussieht.
Erbt man überladene Methoden, kann man einzelne davon überschreiben, ohne die
nicht überschriebenen zu verlieren (Unterschied zu C++).
Für einen Methodenaufruf wird
die Signatur der aufzurufenden Methode zur Übersetzungszeit festgelegt;
die aufzurufende Methode zur Laufzeit.
Grober Ablauf der statischen und dynamischen Methodenbindung
Der Ablauf der statischen und dynamischen Methodenbindung im einzelnen
Wie heißt die aufzurufende Methode und in welcher Klasse (oder
welchem Interface) suchen wir die passende Methoden-Definition?
- Methodenaufruf Methodenname(...), wobei
Methodenname drei Formen haben kann:
- Einfacher Bezeichner: Die Methode heißt Bezeichner
und die Suchklasse ist die, die den Aufruf enthält.
- Qualifizierter Name TypName.Bezeichner: Die Methode heißt
Bezeichner und die Suchklasse ist TypName.
Es ist eine Klassenmethode, daher darf TypName kein Interface
sein.
- Sonst ist die Form FeldName.Bezeichner: Die Methode heißt
Bezeichner und die Suchklasse ist der deklarierte Typ von FeldName.
- Methodenaufruf Ausdruck.Bezeichner(...):
Die Methode heißt
Bezeichner und die Suchklasse
ist der Typ von Ausdruck.
- Methodenaufruf super.Bezeichner(...):
Die Methode heißt
Bezeichner und die Suchklasse
ist die Oberklasse der Klasse, die den Aufruf enthält.
Welche Methodendeklarationen der Suchklasse sind anwendbar und
zugreifbar? Welche von denen ist die speziellste?
Eine Methodendeklaration ist anwendbar auf einen Methodenaufruf, wenn
- die Anzahl der Parameter übereinstimmt
- die Typen der aktuellen Parameter verträglich sind mit denen der
formalen, d.h.:
- die Typen sind gleich
- die Typen der aktuellen Parameter sind durch
Grundtyp-Ausweitung anpaßbar.
- die Typen der aktuellen Parameter sind durch
Referenztyp-Ausweitung anpaßbar.
Eine Methodendeklaration ist zugreifbar für einen
Methodenaufruf, wenn die Zugriffsrechte (Seite
)
es erlauben.
Gibt es mehrere anwend- und zugreifbare Methodendeklarationen,
nimm die speziellste (die mit den ``spezialisiertesten'' Parametertypen):
void test (Ober o1, Ober o2) { ... }
void test (Unter u1, Ober o1) { ... }
void test (Ober o1, Unter u1) { ... }
void test (Unter u1, Unter u2) { ... }
Compiler-Fehlermeldungen gibt es, wenn
- es keine anwend- und zugreifbare Methodendeklaration gibt
- wenn nicht eindeutig ist, welche Methodendeklaration die
speziellste ist.
Wir haben jetzt die Übersetzungszeit-Deklaration der
aufzurufenen Methode. Die muß noch 3 Prüfungen bestehen:
- Wenn der Aufruf die Form Bezeichner hat und in statischem
Kontext (Klassenmethode , statischer
Initialisierungsblock oder
Initialisierer einer Klassenvariablen )
erfolgt, muß die Übersetzungszeit-Deklaration statisch sein.
- Wenn der Aufruf die Form TypName.Bezeichner hat, muß die
Übersetzungszeit-Deklaration statisch sein.
- Wenn die Übersetzungszeit-Deklaration den Typ void hat,
darf sie nur in Kontexten benutzt werden, wo kein Wert erwartet wird.
Welches Objekt soll die Methode ausführen?
- Methodenaufruf Methodenname(...), wobei
Methodenname drei Formen haben kann:
- Einfacher Bezeichner: Wenn Klassenmethode, dann keine
Zielreferenz, sonst der Wert von this.
- Qualifizierter Name TypName.Bezeichner:
Es ist eine Klassenmethode, daher keine Zielreferenz.
- Form FeldName.Bezeichner: Wenn Klassenmethode, dann keine
Zielreferenz, sonst der Wert von FeldName.
- Methodenaufruf Ausdruck.Bezeichner(...):
Wenn Klassenmethode, dann keine
Zielreferenz: werte Ausdruck aus, aber verwerfe Ergebnis.
Sonst werte Ausdruck aus und benutze Ergebnis als Zielreferenz.
- Methodenaufruf super.Bezeichner(...):
Die Zielreferenz ist der Wert von this
Die Argumentausdrücke werden nacheinander von links nach rechts
ausgewertet.
Wenn eine dieser Auswertungen abbricht, wird keins der Argumente
rechts davon ausgewertet (bzw. man merkt nichts davon)
und der gesamte Methodenaufruf wird abgebrochen.
Gibt es es die aufzurufende Methode aus der
Übersetzungszeitdeklaration
überhaupt
noch und darf man auf sie zugreifen?
- Wenn die Methode nicht mehr da ist, gibt es einen
NoSuchMethodError.
- Wenn der Aufruf über ein Interface erfolgte und der
Zielreferenztyp dieses Interface nicht mehr implementiert, gibt es
einen IncompatibleClassChangeError.
- Wenn sich die Zugriffsrechte so verändert haben, daß sie keinen
Zugriff mehr erlauben, gibt es einen IllegalAccessError.
Der Interpretierer merkt sich das Ergebnis der Prüfung solange die
betreffende Klasse geladen bleibt.
Welche Methode soll ausgeführt werden?
- Der Aufruf war statisch: Die aufzurufende Methode ist die,
die der Übersetzer bestimmt hat.
- Wenn eine Zielreferenz da ist und diese den Wert null hat:
NullPointerException.
- Wenn eine Zielreferenz da ist:
- der Aufruf ist nicht virtuell, d.h. kein Überschreiben,
keine dynamische Bindung : Die aufzurufende Methode ist die, die der Übersetzer bestimmt hat
- Der Aufruf ist virtuell, interface:
Die aufzurufenden Methode bestimmt sich aus dem Laufzeit-Typ der Zielreferenz.
- Der Aufruf geschieht über super: Die aufzurufenden Methode
bestimmt sich aus dem Obertyp der Klasse, die den Aufruf enthält.
Next: Die final-Kennzeichnung
Up: Vererbung
Previous: Dynamische Bindung
Peter Pfahler, 1997