Typen von Objekten in Java
Typen von Objekten
ℛ Klassenregel Der Typ eines Objekts ist immer eine Klasse. Die Klasse eines Objekts ist nicht veränderlich. Die Klasse eines Objekts legt fest, welche Einträge das Objekt enthält: Es handelt sich um die nicht-statischen Einträge seiner Klasse.
ℛ Typregel Ausdrücke haben einen Typ, Objekte haben eine Klasse. (Der Typ eines Ausdrucks kann eine Klasse oder eine Schnittstelle sein.)
Wir gebe hier zwei Beispiele für Ausdrücke zusammen mit dem Typ des Ausdrucks und dem Typ des Objektes an, welches der Wert des Ausdruck ist.
Unterschiede zwischen Klassen und Schnittstellen
Wir haben nun auch einen ersten Unterschied zwischen Klassen und Schnittstellen erlernt:
Während der Typ eines Ausdruck eine Klasse oder eine Schnittstelle sein kann, kann der Typ eines Objektes nie eine Schnittstelle sondern immer nur eine Klasse sein.
Zeichenfolgen
Insbesondere für Zeichenfolgenliterale halten wir die wichtige String-Regel fest:
ℛ String-Regel Ein Zeichenfolgenliteral hat den Typ »java.lang.String«. Sein Wert ist ein Objekt, welches die Klasse »java.lang.String« hat. Jenes Objekt stellt die Zeichenfolge des Literals durch ein Objekt dar.
»java.lang.System.out«
ℛ System.out-Regel Der Name »java.lang.System.out« hat den Typ »java.io.PrintStream«. Sein Wert ist ein Objekt, welches die Klasse »java.io.PrintStream« hat. Jenes Objekt stellt das normalerweise zur Ausgabe von Ergebnissen vorgesehene Ausgabesystem dar.
- Ausdrücke, Objekte und Typen
Ausdruck Typ des Ausdrucks Klasse des Objekts
"abc" java.lang.String java.lang.String
java.lang.System.out java.io.PrintStream java.io.PrintStream
Ermittlung des Typs von Ausdrücken
Die Dokumentation von Einträgen (Feldern und Methoden) eines Objektes findet man daher in der Dokumentation der Klasse des Objektes. (Hierdurch wird eine frühere Regel präziert, die sagt, daß man die Dokumentation nicht-statischer Einträge im Typ ihres Ausdrucks findet.)
Der Typ eines Ausdrucks kann der Dokumentation der Standardbibliothek entnommen werden, wenn es sich um einen Namen oder eine Methode der Standardbibliothek handelt.
Ermittlung des Typs von Objekten
Die Klasse eines Objekts kann nur zur Laufzeit ermittelt werden, und zwar, indem man des Objekts Methode »getClass()« aufruft.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( "abc".getClass() );
java.lang.System.out.println( java.lang.System.out.getClass() ); }}transcript
class java.lang.String
class java.io.PrintStream
Ruft man die getClass-Methode eines Objektes auf, so erhält man die Klasse des Objekts.
„Dynamische Typen“
Typen können während der Ausführung eines Programms nicht verändert werden und sind insofern statisch. Sie gehören zum Quelltextmodell. In Java werden mit statisch auch ganz allgemein Dinge bezeichnet, die zum Quelltext oder zu Klassen gehören.
Objekte können während der Ausführung eines Programms neu erzeugt oder verändert werden und sind insofern nicht-statisch. Sie gehören zum Laufzeitmodell. In Java werden mit nicht-statisch oder dynamisch daher auch ganz allgemein Dinge bezeichnet, die zum Laufzeitgeschehen oder zu Objekten gehören.
Obwohl Typen – isoliert betrachtet – statisch sind, bezeichnet man den Typ eines Objektes in Java als Laufzeittyp oder dynamische Typ. Die Typen von Ausdrücken gehören hingegen zum Quelltextmodell. Sie werden daher auch als statische Typen bezeichnet.
Anforderungen an den Objekttyp
Ein Ausdruck liefert eine Liste von Aufrufmöglichkeiten (also von Methodensignaturen für Verbaufrufe, die mit einem Punkt ».« getrennt hinter den Ausdruck geschrieben werden können.)
Das Objekt dieses Ausdrucks kann solche Aufrufe dann erledigen, man sagt es implementiere sie (verwirkliche sie).
Man kann einen Ausdruck mit einer Speisekarte (dem Angebot eines Restaurants) und sein Objekt mit der Küche (der Einrichtung zur Verwirklichung des Angebots) vergleichen. Die Küche muß alles liefern können, was die Speisekarte anbietet. Sie darf auch mehr liefern können, aber nicht weniger.
Ein Ausdruck darf daher nicht mehr Möglichkeiten anbieten als sein Objekt tatsächlich implementiert, daher muß der Typ des Objektes ein Untertyp des Typs des Ausdrucks sein (das Objekt darf höchstens mehr Aufrufe implementieren, aber nicht weniger, als der Ausdruck verspricht).
Es gilt daher die folgende
ℛ Objektregel Die Klasse eines Objektes muß ein Untertyp des Typs eines Ausdrucks (also auch eines Namens) für das Objekt sein.
Das bedeutet nach dem Inklusionsprinzip, daß ein Objekt höchstens mehr Methoden haben darf als sein Ausdruck, aber nicht weniger. Der Compiler prüft beim Übersetzen bereits, ob ein Ausdruck für ein Objekt, eine mit dem Ausdruck als Kontext aufgerufene Methode auch enthält. Die Typanforderung stellt dann sicher, daß die Methode dann auch im Objekt enthalten ist. Daher kann es in Java nicht passieren, daß es dann plötzlich eine Fehlermeldung während der Ausführung eines Programmes gibt, weil eine aufgerufene Methode in einem Objekt fehlt.
Die dadurch gewährleistete statische Typsicherheit der Programmiersprache Java ist ein Hauptgrund dafür, daß Java für größere Programmierprojekte in vielen Organisationen so beliebt ist, denn sie wird nicht von allen anderen konkurrierenden Programmiersprachen geboten. Andererseits erschwert sie das Lernen, da der Programmierer zusätzliche Regeln erlernen muß, die in einigen anderen Sprachen nicht benötigt werden.
- Wenn das Objekt alle Methoden des Ausdrucks enthält, sind alle Methoden, die der Compiler akzeptiert (»f()«, »g()« und »h()«) auch zur Laufzeit möglicht. Es stört dabei nicht, wenn das Objekt noch mehr Methoden (»i()« und »j()«) hat.
Obertyp
Typ des Ausdrucks
| |
| f() g() h() |
| |
-----|-----------------|--------------
Untertyp | f() g() h() | i() j() Typ des Objekts
--------------------------------------
Vergleich
Der Typ eines Ausdrucks verspricht im Quelltext, daß gewisse Verbaufrufe mit dem Ausdruck als Kontext möglich sind (nämlich die Aufrufe nicht-statischer Methoden aus dem Typ des Ausdrucks). Der Ausdruck steht im Quelltext für ein Objekt, das aber erst später (bei der Ausführung des Programms) existieren wird. Wir können den Compiler mit einem Kellner vergleichen, der bestimmte Speisen (Methoden) anbietet.
Der Kellner prüft bei einer Bestellung zunächst, ob das vom Kunden bestellte Gericht auf der Speisekarte des Restaurants ist. Entsprechend prüft der Compiler bei einem Aufruf mit einem Ausdruck als Kontext, ob der Typ des Kontexts eine passende Methode enthält (nicht-statisches Aufrufprinzip). Für den Compiler ist der Typ des Ausdrucks (die Speisekarte) entscheidend. Die Speisekarte entspricht der Dokumentation eines Referenztyps.
Zur Laufzeit (bei der Ausführung des Programms) muß es dann auch ein Objekt hinter dem Ausdruck geben, welches die Methoden wirklich enthält („implementiert“, wie man sagt), also dann die eigentliche Arbeit macht. Das Objekt können wir mit dem Koch vergleichen, der die auf der Speisekarte angepriesenen Gerichte dann kochen (umsetzen, implementieren) können muß.
Es ist zulässig, daß die Küche mehr Gerichte kochen kann als auf der Speisekarte angepriesen, aber sie darf nicht weniger kochen können. Dies entspricht der Objektregel.
Übungsaufgaben
/ Übungsaufgabe
Entnehmen Sie der Dokumentation den Typ des Feldes »java.lang.System.in«.
Ermitteln Sie dann den Typ des Objekts »java.lang.System.in«.
Ist die obenstehende Objektregel erfüllt?
Hilfen:
Der Typ eines Feldes kann der Dokumentation entnommen werden. (Es handelt sich um den Typ eines Ausdrucks für das Feld.)
Die Klasse eines Objektes kann zur Laufzeit ermittelt werden, indem die getClass()-Methode jenes Objektes aufgerufen wird.
Die Objektregel lautet: „Der Typ eines Objektes muß ein Untertyp des Typs eines Ausdrucks (also auch eines Namens) für das Objekt sein.“.