Gültigkeitsbereiche in Java
Der Begriff „Gültigkeitsbereich“ ist ein Begriff des Quelltextmodells.
Der Teil des Quelltextes, in welchem zur Ermittlung der Bedeutung einer Verwendung eines Name (ohne Qualifikation) eine bestimmte Deklaration herangezogen wird, wird der Gültigkeitsbereich jener Deklaration genannt. Wenn keine Mißverständnisse möglich sind, spricht man auch vom Gültigkeitsbereich des (in jener Deklaration deklarierten) Namens oder der Variablen.
Der Gültigkeitsbereich der Deklaration einer innerhalb eines Blocks deklarierten Variablen reicht von der Stelle direkt nach dem Gleichheitszeichen in der Deklaration bis zum Ende des innersten die Deklaration direkt enthaltenden Blocks.
Die Variablendeklaration »final int zwoelf = 12;« bedeutet, daß der Bezeichner »zwoelf« in seinem Gültigkeitsbereich (also im Rest des Blockes) als Ausdruck mit dem Typ »int« und dem Wert »12« verwendet werden können soll.
Erneute Deklarationen
In einem Block darf jeder Name nur einmal deklariert werden.
Main.txt
public final class Main
{ public static void main( java.lang.String[] args )
{ final int i = 22; java.lang.System.out.println( i );
final int i = 22; java.lang.System.out.println( i ); }}Protokoll
Main.java:4: error: variable i is already defined in method main(String[])
final int i = 22; java.lang.System.out.println( i ); }}
^
1 error
Bei Fehlern durch unerlaubte Mehrfachdeklaration ein und desselben Namens spricht man auch von einem Zusammenstoß oder einer Kollision der Namen (Namenskollision).
Begrenzungen der Gültigkeit im Quelltext
Eine Deklaration gilt nur für den Rest des sie direkt enthaltenden Blocks (ab dem Gleichheitszeichen). Da der Gültigkeitsbereich einer solchen in einem Block deklarierten Variablen auf den relativ kleinen Bereich des Blockes eingeschränkt ist, wird solch eine Deklaration auch eine lokale Deklaration und solch eine Variable auch eine lokale Variable genannt.
Main.txt
public final class Main
{ public static void main( final java.lang.String[] args )
{{ final int n = 7;
java.lang.System.out.println( n ); }java.lang.System.out.println( n ); }}
Protokoll
Main.txt:8: error: cannot find symbol
java.lang.System.out.println( n ); }}
^
symbol: variable n
location: class Main
1 error
Entsprechend darf eine in einem inneren Block schon einmal deklarierte Variable in einem äußeren Block deklariert werden, wenn sich die Deklaration in dem äußeren Block hinter der Deklaration in dem inneren Block befindet.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{{ final int n = 7;
java.lang.System.out.println( n ); }final int n = 4;
java.lang.System.out.println( n ); }}Protokoll
7
4
Wir behandeln in dieser Lektion nur innerhalb eines Blocks deklarierte Variablen, die wir auch manchmal Blockvariablen nennen.
Man sagt auch, daß man durch eine Deklaration einen Namen in einen Gültigkeitsbereich oder einen Block einführe (im Sinne von „Hinzufügen“), oder man sagt zu einer Festlegung einer Variablen auch „Einführung einer Variablen“.
Nichtüberlappende Gültigkeitsbereiche
Blöcke können einander folgen. Jeder Block ist ein eigener Namensraum und kann daher eigene lokale Variablen enthalten, deren Namen sich nicht von den Namen lokaler Variablen in anderen nicht-überlappenden Gültigkeitsbereichen unterscheiden müssen.
final-Variablen werden ja manchmal auch als „Konstanten“ bezeichnet. Man könnte dazu einwenden, die Konstante »i« sei hier gar keine richtige Konstante, da sie zuerst 3 und dann 7 sei. Tatsächlich handelt es sich hierbei aber um zwei verschiedene Konstanten, die nur den gleichen Bezeichner »i« miteinander teilen. Sie sind aber unterschiedliche Konstanten, da sie in zwei unterschiedlichen, nichtüberlappenden Gültigkeitsbereichen existieren.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ /*.------------------------------------.
| |
| v */
{ final int i = 3; java.lang.System.out.println( i ); } /*.------------------------------------.
| |
| v */
{ final int i = 7; java.lang.System.out.println( i ); }}}
Protokoll
3
7
Die Identität einer Variablen ist erst durch ihren Namen und ihren Gültigkeitsbereich gemeinsam gegeben. Zwei Variablen mit unterschiedlichen Gültigkeitsbereichen sind also zwei verschiedene Variablen, auch wenn sie denselbe Namen haben.
Wenn der Gültigkeitsbereich eines Namens klein ist (wie in dem obigen Programmbeispiel), ist es eher möglich, daß der Name bei Bedarf in verschiedenen Programmteilen verwendet werden kann, ohne daß es zu Namenskollisionen kommt. Daher sollte der Gültigkeitsbereich eines Namens immer möglichst klein sein.
Verschachtelte Bereiche
Ein innerer Block ist ein eigener Gültigkeitsbereich. Er übernimmt („erbt“) alle Deklarationen umgebender Gültigkeitsbereiche, schließlich ist er ja im wörtlichen Sinne in diesen enthalten, also Teil von ihnen.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final int i = 22; { java.lang.System.out.println( i ); }}}java.lang.System.out
22
In einem inneren Block dürfen lokale Bezeichner eines äußeren Blocks nicht erneut deklariert werden.
Main.txt
public final class Main
{ public static void main( java.lang.String[] args )
{ final int i = 22;
{ final int i = 23; java.lang.System.out.println( i ); }}}Protokoll
Main.java:4: error: variable i is already defined in method main(String[])
{ final int i = 23; java.lang.System.out.println( i ); }}}
^
1 error
Richtung einer Deklaration ⃗
Eine lokale Variable kann nicht vor der Stelle ihrer Deklaration verwendet werden, auch wenn die Verwendung im selben Block erfolgt.
Main.txt
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( i ); final int i = 22; }}Protokoll
Main.java:3: error: cannot find symbol
{ java.lang.System.out.println( i ); final int i = 22; }}
^
symbol: variable i
location: class Main
1 error
Unterscheidung zu Namen aus Klassen ⃗
Es ist erlaubt, einen Namen, der schon in einer Klasse der Standardbibliothek deklariert wurde, in einem Block erneut zu deklarieren.
Der Block in dem folgenden Programm gehört nicht zum Gültigkeitsbereich des Namens »PI« aus der Klasse »java.lang.Math«, da der Name im Block nicht ohne Qualifikation verwendet werden kann. Daher kann in diesem Block ein lokaler Name »PI« deklariert werden. Durch die Qualifikation können beiden Namen danach unterschieden werden.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{final double PI = 3.1415926535897932384626433832795;
java.lang.System.out.println( PI );
java.lang.System.out.println( java.lang.Math.PI ); }}
transcript
3.141592653589793
3.141592653589793
Anders als in dem obigen Beispielprogramm gezeigt, sollten aber in einem Block deklarierte final-Variablen normalerweise nicht groß geschrieben werden.
Übungsfragen ⃗
? Gültigkeitsbereiche ⃗
Kann das folgende Programm ohne Fehlermeldungen gestartet werden, und was gibt es dann gegebenenfalls aus?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{{ final int i = 4; java.lang.System.out.println( i ); }
{ final int j = 1; java.lang.System.out.println( j ); } }}
? Gültigkeitsbereiche ⃗
Kann das folgende Programm ohne Fehlermeldungen gestartet werden, und was gibt es dann gegebenenfalls aus?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{{ final int i = 5; java.lang.System.out.println( i ); }
{ final int i = 0; java.lang.System.out.println( i ); } }}
? Gültigkeitsbereiche ⃗
Kann das folgende Programm ohne Fehlermeldungen gestartet werden, und was gibt es dann gegebenenfalls aus?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{{ final int i = 2; java.lang.System.out.println( i ); }
final int i = 4; java.lang.System.out.println( i ); }}
? Gültigkeitsbereiche ⃗
Kann das folgende Programm ohne Fehlermeldungen gestartet werden, und was gibt es dann gegebenenfalls aus?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{final int i = 7; java.lang.System.out.println( i );
{ final int i = 8; java.lang.System.out.println( i ); } }}
? Gültigkeitsbereiche ⃗
Kann das folgende Programm ohne Fehlermeldungen gestartet werden, und was gibt es dann gegebenenfalls aus?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{final int i = 8; java.lang.System.out.println( i );
{ java.lang.System.out.println( i ); } }}
Übungsaufgaben ⃗
/ Refaktor ⃗
Setzen Sie die beiden Paare von Anweisungen, die im folgenden Programm enthalten sind, jeweils in geschweifte Klammern und benennen Sie »text1« dann in »text« um. Vergewissern Sie sich, daß das Programm hinterher weiterhin mit derselben Ausgabe läuft.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{final java.lang.String text = "text";
java.lang.System.out.println( text );final java.lang.String text1 = "text";
java.lang.System.out.println( text1 );}}
transcript
text
text
(Wenn sich in einem Programmteil eine Beinahe-Wiederholung eines Programmteils findet, sollte diese in eine wörtliche Wiederholung umgewandelt werden. In manchen Fällen ist es dann nämlich möglich, die beiden Wiederholungen zu einem Programmteil zusammenzufassen, wie später zu sehen sein wird.)
Ein Anwendungsbeispiel *
Wenn Büromöbel von einem Endverbraucher erworben werden, sind im Jahre 2003 noch 16 % Mehrwertsteuer zu zahlen. Es werden zwei Tische für je 420 Euro und eine Lampe für 145 Euro Warenwert erworben und 3 % Rabatt vereinbart. Wieviel Euro sind zu zahlen?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args ){ // ewige Konstanten; "Welt"
final double prozent = 0.01;
final double euro = 1.;{ // aktuelle Konstanten; "Bundesrepublik Deutschland"
final double mwSt = 16. * prozent;{ // Warenwerte (Preisliste); "Dieses Geschäft"
final double tischwert = 420 * euro;
final double lampenwert = 145 * euro;{ // Ein Verkauf in diesem Geschäft
final double rabatt = 3. * prozent;
final double wert = 2 * tischwert + 1 * lampenwert;
final double rabattiert = wert *( 100. * prozent - rabatt );
final double brutto = rabattiert *( 100. * prozent + mwSt );
final double gerundet = Math.round( brutto * 100. )/ 100.;
final double forderung = gerundet / euro;java.lang.System.out.println( "Zu zahlen sind " + forderung + " Euro." ); }
{ // Ein anderer Verkauf in diesem Geschäft
final double rabatt = 0. * prozent;
final double wert = 1 * lampenwert;
final double rabattiert = wert *( 100. * prozent - rabatt );
final double brutto = rabattiert *( 100. * prozent + mwSt );
final double gerundet = Math.round( brutto * 100. )/ 100.;
final double forderung = gerundet / euro;java.lang.System.out.println( "Zu zahlen sind " + forderung + " Euro." ); }}}}}
Protokoll
Zu zahlen sind 1108.32 Euro.
Zu zahlen sind 168.2 Euro.
Das Geschäft ist ein Teil der Bundesrepublik Deutschland und die Bundesrepublik Deutschland ist ein Teil der Welt. Genauso sind auch die entsprechenden Blöcke in dem obigen Programm ineinander verschachtelt. Das ist nicht nur eine schöne Analogie, sondern hat auch den praktischen Nutzen, daß ein weiteres „Land“, das in dem Programm im äußeren Hauptblock angelegt wird, der die Welt repräsentiert, alle „Weltkonstanten“ von diesem Block übernimmt, aber eine eigene Mehrwertsteuer haben kann.
Dieses Prinzip wird anhand der beiden Verkäufe dargestellt: Da sie beide zum selben Geschäft gehören, „erben“ beide die Preisliste aus dem umgebenden Block, der das Geschäft repräsentiert. Jeder Verkauf hat jedoch seinen eigenen Warenwert, Rabatt und schließlich seine eigene Forderung.
Zitate *
- JLS 10 6.3
- The scope of a declaration is the region of the program within which the entity declared by the declaration can be referred to using a simple name, provided it is not shadowed (§6.4.1).
- JLS 7 6.3 und JLS 10 6.3
- The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.