Aufrufe mit Kontext in Java (Aufrufe mit Kontext in Java), Lektion, Seite 722339
https://www.purl.org/stefan_ram/pub/kontext_java (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
Java-Kurs

Aufrufe nicht-statischer  Ausdruckmethoden

Motivation Mindestens 80 Prozent aller Methoden der Standardbibliothek sind nicht-statische  Methoden. Man kann Java  also nur richtig nutzen, wenn man sich auch mit nicht-statischen  Methoden auskennt.

Aufrufe nicht-statischer Methoden  sind das Thema dieser Lektion.

»java.lang.System.out« als Feld [d]

Statische Felder, wie »java.lang.Math.PI«, wurden bereits behandelt.

Dokumentation von »java.lang.Math.PI« (gekürzt, vereinfacht und übersetzt)
Modul java.base
Packet java.lang
Klasse Math
Feldzusammenfassung
static double PI
Die Kreiszahl ‹ π ›.

Auch »java.lang.System.out« bezeichnet ein statisches Feld, nämlich ein statisches Feld im Typ »java.lang.System«. Während der Typ von »java.lang.Math.PI« jedoch ein elementarer Typ  ist, ist der Typ von »java.lang.System.out« ein Referenztyp.

Um die Dokumentation zu »java.lang.System.out.println( "Hallo, Welt" )« zu finden, müssen wir zuerst den Typ des Kontexts  ermitteln. Der Kontext ist »java.lang.System.out«. Dieser Kontext ist ein Feld namens »out« aus dem Referenztyp »java.lang.System«. Den Typ dieses Feldes finden wir also in der Dokumentation des Typs »java.lang.System«.

»java.lang.System.out« (überarbeitet und übersetzt)

java.lang.System.out

static java.io.PrintStream out

Der Standardausgabestrom.

Das Feld »out« ist ein Teil des Typs »java.lang.System«, aber sein Typ ist nicht  »java.lang.System«, sondern »java.io.PrintStream«. Das Feld »java.lang.System.out« hat also eine direkte Beziehung zu zwei Typen : Es ist im Typ »java.lang.System« enthalten, aber seine Methoden sind in der Dokumentation des Typs »java.io.PrintStream« dokumentiert.

Der Dokumentation können wir nun entnehmen, daß der Typ des Feldes »java.lang.System.out« gleich »java.io.PrintStream« ist. Die Dokumentation der mit diesem Kontext aufgerufenen Methode finden wir also in der Dokumentation des Typs »java.io.PrintStream«.

Das folgende Programm zeigt, wie der Typ eines Ausdrucks auch mit einem Java -Programm ermittelt werden kann. Bei vielen Java -Implementation erscheint der Typ des Ausdrucks »java.lang.System.out« innerhalb einer vom Compiler ausgegebenen Fehlermeldung.

Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.error(); }}
transcript
Main.java:3: error: cannot find symbol
{ java.lang.System.out.error(); }}
^
symbol: method error()
location: variable out of type PrintStream
1 error
Aussprachehinweis
symbol  ˈsɪmbəl

Man kann den Wert dieses Feldes auch ausgeben, doch ist die Ausgabe nicht besonders verständlich oder informativ.

Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( java.lang.System.out ); }}
java.lang.System.out
java.io.PrintStream@1e30857

Erkennen nicht-statischer Methoden in der Dokumentation [d]

Eine nicht-statische Methode  erkennt man daran, daß in der Proklamation vor ihrem Namen nicht  »static« steht.

Als Beispiel einer Dokumentation einer nicht-statischen Methode zeigen wir hier die Kurzdokumentation der nicht-statischen Methode »println()« im Typ »java.io.PrintStream«.

Die Dokumentation der nicht-statischen Methode »println()« im Typ »java.io.PrintStream« (Java 10 )
void println()
Terminates the current line by writing the line separator string.

Ein Beispiel eines Aufrufs einer nicht-statischen Methode

Ein Ausdruck als Kontext erlaubt es jedoch zusätzlich auch nicht-statische  Methoden seines Typs aufzurufen.

Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( "7".valueOf( 3 ));
java.lang.System.out.println( "abcd".indexOf( "c" )); }}
Protokoll
3
2

Der Aufruf einer nicht-statischen  Methode besteht, wie Aufrufe statischer Methoden (wenn es sich nicht um Verbaufrufe handelt), aus einem Kontext, einem Punkt, und einem Verbaufruf.

(Der Kontext und der Verbaufruf in dem folgenden Syntaxdiagramm gibt jeweils ein Beispiel wieder, aber ist nicht als eine allgemeine Definition von „Kontext“ und „Verbaufruf“ zu verstehen.)

Analyse des Aufrufs »"abcd".indexOf( "c" )« ::=

Ausdruck
.---------. .-. .------------.
--->| Kontext |--->( . )--->| Verbaufruf |--->
'---------'  '-' '------------'

Kontext
.------.
--->( "abcd" )--->
'------'

Verbaufruf
.-------. .-. .---. .-.
--->( indexOf )--->( ( )--->( "c" )--->( ) )--->
'-------' '-' '---' '-'

Die nicht-statische Methode »indexOf(java.lang.String)« ergibt die Position ihres Arguments im Kontext, hier also die Position des »a« in der Zeichenfolge »abcd«. Das Ergebnis lautet »0« und nicht »1«, weil das Zählen in diesem Falle mit der Null begonnen wird.

Die objektorientierte Programmierung ist durch die Verwendung nicht-statischer Methoden  gekennzeichnet.

Eine nicht-statische Methode kann nicht mit einem Typ als Kontext aufgerufen werden.

Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( java.lang.System.getProperties() );
java.lang.System.out.println( java.lang.String.valueOf( 3 ));
java.lang.System.out.println( java.lang.String.indexOf( "c" )); }}
Protokoll
Main.java:4: error: non-static method indexOf(String) cannot be referenced from a static context
java.lang.System.out.println( java.lang.String.indexOf( "c" )); }}
^
Protokoll (übersetzt)
Main.java:4: Fehler: nicht-statische Methode indexOf(String) kann nicht über statischen Kontext referenziert werden
java.lang.System.out.println( java.lang.String.indexOf( "c" )); }}
^

Man beachte, daß die originale Fehlermeldung des Java -Compilers (aus dem JDK9) genau die  Begriffe verwendet, die wir auch in diesem Kurs verwenden: »non-static method« und »static context«.

Ein weiteres Beispiel eines Aufrufs einer nicht-statischen Methode

In einem Aufruf wie »java.lang.System.out.println()« gehört das Verb »println« nicht zu einer statischen Methode. Es handelt sich vielmehr um eine nicht-statische Methode.

Analyse des Aufrufs »java.lang.System.out.println()« ::=

Ausdruck
.---------. .-. .------------.
--->| Kontext |--->( . )--->| Verbaufruf |--->
'---------'  '-' '------------'

Kontext
.----. .-. .----. .-. .------. .-. .---.
--->( java )--->( . )--->( lang )--->( . )--->( System )--->( . )--->( out )--->
'----' '-' '----' '-' '------' '-' '---'

Verbaufruf
.-------. .-. .-.
--->( println )--->( ( )--->( ) )--->
'-------' '-' '-'

?    Beispiel
java.lang.System.out . println()

In »java.lang.System.out.println()« wird die Methode »println()« des  Ausdrucks »java.lang.System.out« aufgerufen.

Der Kontext des Verbaufrufs »println()« ist hier kein  Referenztypname (wie bisher), sondern der Ausdruck »java.lang.System.out«.

»java.lang.System.out« ist der Name eines Feldes genauso wie »java.lang.Math.PI« – nur ist der Typ von »java.lang.Math.PI« der elementare Typ »double«, während der Typ von »java.lang.System.out« der Referenztyp »java.io.PrintStream« ist.

Dokumentation von »java.lang.Math.PI« (gekürzt, vereinfacht und übersetzt)
Modul java.base
Packet java.lang
Klasse Math
Feldzusammenfassung
static double PI
Die Kreiszahl ‹ π ›.
Der Name »java.lang.Math.PI« steht zu zwei Typen in einer engen Beziehung
                 enthaltender Typ                                                Typ
java.lang.Math <------------------------ java.lang.Math.PI -------------------------> double
»java.lang.System.out« (überarbeitet und übersetzt)

java.lang.System.out

static java.io.PrintStream out

Der Standardausgabestrom.

Der Name »java.lang.System.out« steht zu zwei Typen in einer engen Beziehung
                   enthaltender Typ                                                    Typ
java.lang.System <------------------------- java.lang.System.out -------------------------> java.io.PrintStream

(Zwar findet sich das Symbol »Name« in unserem Syntaxdiagramm für »Ausdruck«, doch nicht jeder  Name ist ein Ausdruck: Namen von Paketen, Typen und Methoden sind keine Ausdrücke.)

Der Typ von »java.lang.Math.PI« ist nicht »java.lang.Math«, und genauso ist der Typ von »java.lang.System.out« auch nicht »java.lang.System«.

»java.lang.Math.PI« ist kein Typname, obwohl es mit dem Typnamen »java.lang.Math« beginnt, sondern der Name eines Feldes. »java.lang.Math.PI« ist ein Ausdruck. Typen und Ausdrücke sind streng getrennt: ein Ausdruck ist nie ein Typ und ein Typ ist nie ein Ausdruck.

Daß »java.lang.System.out« kein  Name eines Referenztyps, sondern eine Name eines Feldes ist, kann der Dokumentation entnommen werden (es folgt nicht aus der Java -Syntax alleine).

Der Kontext  eines Aufrufs einer nicht-statischen Methode ist ein Ausdruck, wie beispielsweise »java.lang.System.out«.

Der Verbaufruf »println()« hat nicht  den Typ »java.lang.System« als Kontext, denn zwischen beidem steht ja noch ».out.«!

Daß »java.lang.System.out.println« kein  Name einer Methode ist, sondern eine Kombination aus dem Namen eines Feldes, einem Punkt und einem Verb, kann der Dokumentation entnommen werden (es folgt nicht  aus der Java -Syntax alleine): Der Name eines Feldes kann (anders als der Name einer Referenztyps) nicht durch einen nachgestellten Namen zu einem neuen Namen erweitert werden. Ein nachgestelltes ».println« kann diesen Namen deswegen nicht  zu einem noch längeren Namen erweitern. Im Ausdruck »java.lang.System.out.println()« ist »java.lang.System.out.println« also kein  Name! Der Ausdruck muß vielmehr zerlegt werden in den Namen »java.lang.System.out« und den Verbaufruf »println()«. Der Feldname ist darin der Kontext für den Verbaufruf. Die Groß- und Kleinschreibung liefert allerdings schon ohne das Lesen der Dokumentation ein Indiz dafür, daß »java.lang.System« ein Name eines Referenztyps und »java.lang.System.out« kein  Name eines Referenztyps ist.

Solch einen Ausdruck als Kontext (einen Kontextausdruck ) nennt man auch einen nicht-statischen Kontext , weil sein Wert ja erst zur Laufzeit festgelegt wird, er ist also nicht statisch (also nicht schon durch den Quelltext festgelegt).

Der Typ  solch eines Kontextausdrucks muß ein sogenannter Referenztyp  (wie beispielsweise eine Klasse oder eine Schnittstelle) sein. Ein Referenztyp kann daran erkannt werden, daß sein eigentlicher Name groß geschrieben wird.

Einen Ausdruck, dessen Typ ein Referenztyp ist, nennen wir auch einen Referenzausdruck.

Der Aufruf »2.println()« ist beispielsweise nicht erlaubt, weil der Typ des Kontextausdrucks »2« kein Referenztyp ist. Vergleichsweise ist aber der Aufruf »"".valueOf( 3 )« gestattet, weil der Typ des Kontextausdrucks »""« ein Referenztyp ist.

Tabelle
Aufruf ------------------------------------.   Beschreibung --------------------------------------------------------.
| |
Kontext Punkt Verbaufruf Kontextgattung Typ des Kontext Kontextart Methodenart java.lang.Thread . dumpStack() Referenztypname - statisch statisch
java.lang.Math . random() Referenztypname - statisch statisch
java.lang.Math . floor( 2.7 ) Referenztypname - statisch statisch
java.lang.String . valueOf( 3 ) Referenztypname - statisch statisch "" . valueOf( 3 ) Ausdruck java.lang.String nicht-statisch statisch "abcd" . indexOf( "c" ) Ausdruck java.lang.String nicht-statisch nicht-statisch
java.lang.System.out . println() Ausdruck java.io.PrintStream nicht-statisch nicht-statisch
java.lang.System.out . println( 2 ) Ausdruck java.io.PrintStream nicht-statisch nicht-statisch

Während »java.lang.Thread.dumpStack« ein Methodenname ist, gilt »java.lang.System.out.println« nicht  als Methodenname, da der letzte Punkt darin eine Form der Verbindung repräsentiert, die in Namen nicht erlaubt ist (denn vor ihm steht bereits ein maximal qualifizierter Name).

Bei Aufrufen statischer  Methoden dient der Name eines Referenztyps  (oder – selten – ein Ausdruck) als Kontext. Eine Referenztypname bildet einen statischen Kontext. Ein isoliert betrachteter Verbaufruf ist ein Aufruf ohne Kontext.

Methoden, die mit einem Referenztypnamen als Kontext aufgerufen werden können, wie die Methode »java.lang.Thread.dumpStack()«, werden auch als statische Methoden  bezeichnet. Methoden, die mit einem Ausdruck als Kontext aufgerufen werden können, wie die Methode »java.lang.System.out.println()« sind dementsprechend nicht-statische Methoden.

Ein Referenztypname  ist immer der Kontext eines statischen  Aufrufs.
Ein Ausdruck  ist der übliche Kontext eines nicht-statischen  Aufrufs.

Allgemein kennzeichnet das Adjektiv „statisch“ bereits durch den Quelltext (bei der „Übersetzungszeit“) Festgelegtes oder Gehörendes (wie Referenztypen) und das Adjektiv „nicht-statisch“ oder „nichtstatisch“ (auch: „dynamisch“) erst bei der Programmausführung (bei der „Laufzeit“) Festgelegtes oder Gehörendes (wie Werte von Ausdrücken).

Daß ein nicht-statischer Kontext ein Ausdruck ist, kann man auch daran erkennen, daß man ihn – wie alle Ausdrücke – einklammern  kann: »( java.lang.System.out ).println()«. Ein Referenztypname ist hingegen kein  Ausdruck und kann nicht eingeklammert werden: »( java.lang.Thread ).dumpStack()« ist also nicht erlaubt. Genauso sind auch Methodennamen keine  Ausdrücke: »( java.lang.Thread.dumpStack )()« ist also ebenfalls nicht erlaubt.

Feldnamen sind Ausdrücke, Typnamen und Methodennamen nicht.

Hier geben wir die obenstehende Regel noch einmal in einer kurzen Merkform an:

Referenztypname: statischer  Kontext
Ausdruck: nicht-statischer  Kontext

»out« ist ein statisches  Feld, es steht in »java.lang.System.out« direkt hinter einem Typnamen. Obwohl das Feld »out« als Feld des Typs »java.lang.System« das Attribut „statisch “ hat, gilt ein Ausdruck für dieses Feld als ein nicht-statischer Kontext, weil Ausdrücke stets nicht-statische Kontexte sind.

»println« ist eine nicht-statische  Methode, ihr Name steht in »java.lang.System.out.println« direkt hinter einem Ausdruck.

Aufruf einer Methode, die übliche Dichotomie ::=

.-------------. .---. .-------------------------------------------.
--->| Referenztyp |--->( . )--->| Verbaufruf einer statischen Methode |--->
'-------------'  '---' '-------------------------------------------'

.-------------. .---. .-------------------------------------------.
--->| Ausdruck |--->( . )--->| Verbaufruf einer nicht-statischen Methode |--->
'-------------'  '---' '-------------------------------------------'

technisch möglich (wie bei »"".valueOf( 3 )«) aber unüblich ::=
    .-------------.     .---.     .-------------------------------------------.
--->| Ausdruck |--->( . )--->| Verbaufruf einer statischen Methode |--->
'-------------'  '---' '-------------------------------------------'

Ausgabeanweisungen

Da wir im Grundkurs Aufrufe nicht-statischer Methoden noch nicht weiter behandeln wollten, wurden dort extra „Ausgabeanweisungen“ eingeführt. Nun haben wir gelernt, daß »java.lang.System.out.println()« ein Ausdruck ist. Damit können wir nun erkennen, daß die bisherigen „Ausgabeanweisungen“ in Wirklichkeit nichts weiter als Aufrufanweisungen sind. Deswegen ist es nun nicht mehr nötig weiterhin, von „Ausgabeanweisungen“ zu sprechen. Es spricht aber auch nichts dagegen, auch weiterhin gelegentlich eine Aufrufanweisung, die etwas ausgibt, als eine „Ausgabeanweisung“ zu bezeichnen.

Syntax

Das folgende Syntaxdiagramm für Ausdrücke wurde um die Syntax für Aufrufe nicht-statischer Methoden erweitert.

Neue, erweiterte Syntax (vereinfacht)

Ausdruck
.----------.
---.----------->| Literal |---------------------------.---->
| '----------' |
| .----------. |
'----------->| Name |---------------------------'
| '----------' |
| .-. .----------. |
'--->( - )-->| Ausdruck |---------------------------'
| '-' '----------' |
| .-. .----------. |
'--->( + )-->| Ausdruck |---------------------------'
| '-' '----------' |
| .-. .----------. .-. |
'--->( ( )-->| Ausdruck |-->( ) )-------------------'
| '-' '----------' '-' |
| .----------. .-. .----------. |
'----------->| Ausdruck |-->( / )-->| Ausdruck |----'
| '----------' '-' '----------' |
| .----------. .-. .----------. |
'----------->| Ausdruck |-->( + )-->| Ausdruck |----'
| '----------' '-' '----------' |
| .----------. .-. .----------. |
'----------->| Ausdruck |-->( - )-->| Ausdruck |----'
| '----------' '-' '----------' |
| .----------. .-. .----------. |
'----------->| Ausdruck |-->( * )-->| Ausdruck |----'
| '----------' '-' '----------' |
| .---------------------------------. |
'----------->| Aufruf einer statischen Methode |----'
| '---------------------------------' |
| .---------------------------. |
'----------->| nicht-statischer Aufruf |----------'
'---------------------------'

Name
.-----------------------.
| .------. .-. v .------------------------.
---'--->| Name |-->( . )---'--->| Bezeichnerzeichenfolge |--->
'------' '-' '------------------------'

Ausdruckliste
.---------------------------.
| .----------. v
---'---.--->| Ausdruck |---.---'--->
^ '----------' |
| .-. |
'-------( , )<------'
'-'

Aufruf einer statischen Methode
.------. .-. .---------------. .-.
--->| Name |--->( ( )--->| Ausdruckliste |--->( ) )--->
'------' '-' '---------------' '-'

nicht-statischer Aufruf
.-----------. .-. .------------. .-. .---------------. .-.
--->| Ausdruck |--->( . )--->| Bezeichner |--->( ( )--->| Ausdruckliste |--->( ) )--->
'-----------' '-' '------------' '-' '---------------' '-'

Nicht-syntaktische Anforderungen
Der »Name« in der Produktionsregel »Ausdruck« muß ein Name einer Variablen sein (Namen von Typen oder Methoden sind keine Ausdrücke).
Der »Name« in der Produktionsregel »Aufruf einer statischen Methode« muß ein Name einer Methode sein.

Übungsfragen

?   Kontext

Würde es sich bei den folgenden Texten, wenn man sie als Kontext verwenden würde, jeweils um einen statischen  oder um einen nicht-statischen  Kontext handeln?

?   Kontext (1)

Warum würde der folgende Aufruf, selbst wenn er erlaubt wäre, keinen Sinn ergeben?

Aufruf
java.lang.String.indexOf( "c" )

Anmerkungen und Zitate *

Syntax eines Aufrufs einer statischen  Methode *

Zitate aus der JLS, leicht umgestellt und vereinfacht:

Aufruf einer statischen Methode
MethodInvocation 〉 ::=
TypeName 〉 "." 〈Identifier 〉 "(" [〈ArgumentList 〉] ")".
Aufruf einer nicht-statischen Methode
MethodInvocation 〉 ::=
Expression 〉 "." 〈Identifier 〉 "(" [〈ArgumentList 〉] ")".

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram722339 stefan_ram:722339 Aufrufe mit Kontext in Java Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd722339, slrprddef722339, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/kontext_java