Referenzparameter in Java
Der Typ eines Parameters kann auch ein Referenztyp sein. Dies wurde ja schon im Grundkurs behandelt.
So ist es beispielsweise möglich eine statische Methode zu definieren, die einen Text in Anführungszeichen ausgibt.
Main.java
public class Main
{ public static void printTag( final java.lang.String text )
{ java.lang.System.out.print( "<" + text + ">" ); }
public static void main( final java.lang.String[] args )
{ printTag( "TABLE" ); java.lang.System.out.println(); }}Main [Kompassdiagramm]
.-----------------.
String | Main |
-------------->| |
text | |
'-----------------'
|
V
Ausgabe des Texts
in spitzen KlammernSystem.out
<TABLE>
Referenzeffekt: Die Nullreferenz als Argument
Der Aufbaukurs konnte bisher gut ohne Kenntnisse von Verzweigungsanweisungen (»if«) und Schleifenanweisungen (»while«) gelesen werden. Ab hier werden diese nun manchmal als bekannt vorausgesetzt. Weiter oben, im Grundkurs, befindet sich ein Kapitel zu Verzweigungsanweisungen (»if«) und Schleifenanweisungen (»while«).
Wenn eine Methode mit »null« aufgerufen wird, dann kann dies zu einem Laufzeitfehler führen.
Main.java
public class Main
{public static void method( final java.lang.String string )
{ java.lang.System.out.println( string.length() );
java.lang.System.out.println( "println" ); }public static void main( final java.lang.String[] args )
{ method( "abc" );
method( null );
java.lang.System.out.println( "println" ); }}Protokoll
3
println
Exception in thread "main" java.lang.NullPointerException
at Main.method(Main.java:5)
at Main.main(Main.java:10)
Die obigen Methodendeklaration ist in Ordnung, es muß jedoch dann dokumentiert werden (wie dies geht, wird später behandelt werden), daß method nicht mit »null« aufgerufen werden darf.
Sonst wäre eine „nullsichere“ Variante möglich:
Main.java
public class Main
{ public static void method( final java.lang.String string )
{ if( string != null )java.lang.System.out.println( string.length() ); }
public static void main( final java.lang.String[] args )
{ method( null ); }}java.lang.System.out
- (keine Ausgabe)
Aber auch bei dieser Variante muß das Verhalten für null dokumentiert werden.
Wir können zwei Dinge festhalten:
Erstens Eine Methodendeklaration ist nicht vollständig, solange nicht auch die Dokumentation zu der Methode geschrieben wurde. Wie dies geht wurde aber in diesem Kurs bisher leider noch nicht gezeigt.
Zweitens Der Autor einer Methodendeklaration muß es sich bewußt machen, daß jedes Referenzargument auch den Wert »null« haben könnte und bewußt festlegen und dokumentieren, wie sich die Methode dann verhält.
Referenzeffekt: Aliasparameter
Wenn eine Methode mehrere Referenzparameter hat, dann kann es sein, daß zwei davon die gleiche Referenz enthalten. Man sollte also nicht davon ausgehene, daß alle Parameter unterschiedliche Referenzen enthalten. Auch hier sollte dokumentiert werden, ob es gestattet ist, daß ein Referenzwert mehrfach übergeben wird, und wie sich die Methode dann verhält.
Main.java
public final class Main
{
static void copy
( final java.lang.StringBuilder target,
final java.lang.StringBuilder source )
{ target.setLength( 0 );
target.append( source ); }
public static void main( final java.lang.String[] args )
{ final java.lang.StringBuilder alpha = new java.lang.StringBuilder( "alpha" );
final java.lang.StringBuilder gamma = new java.lang.StringBuilder( "gamma" );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" );
java.lang.System.out.println( "gamma = \"" + gamma + "\"" );
java.lang.System.out.println();
copy( alpha, gamma );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" );
java.lang.System.out.println( "gamma = \"" + gamma + "\"" );
java.lang.System.out.println();
copy( alpha, alpha );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" ); }}
transcript
alpha = "alpha"
gamma = "gamma"
alpha = "gamma"
gamma = "gamma"
alpha = ""Main.java
public final class Main
{
static void copy
( final java.lang.StringBuilder target,
final java.lang.StringBuilder source )
{ if( target == source )return;
target.setLength( 0 );
target.append( source ); }
public static void main( final java.lang.String[] args )
{ final java.lang.StringBuilder alpha = new java.lang.StringBuilder( "alpha" );
final java.lang.StringBuilder gamma = new java.lang.StringBuilder( "gamma" );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" );
java.lang.System.out.println( "gamma = \"" + gamma + "\"" );
java.lang.System.out.println();
copy( alpha, gamma );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" );
java.lang.System.out.println( "gamma = \"" + gamma + "\"" );
java.lang.System.out.println();
copy( alpha, alpha );
java.lang.System.out.println( "alpha = \"" + alpha + "\"" ); }}
transcript
alpha = "alpha"
gamma = "gamma"
alpha = "gamma"
gamma = "gamma"
alpha = "gamma"
Datenaufrufe ⁺
Das folgende Programm zeigt noch einmal wie Objekte den Aspekt des Datums (Wertes) mit dem des Programms vereinigen:
Bei Aufruf von »printLength« wird ein Objekt wie ein Datum (Wert) als Argument übergeben. In der Methode »printLength« wird es dann durch Aufruf seiner length -Methode wie ein Programm aktiviert.
Main.java
public class Main
{ public static void printLength( final java.lang.String string /* "Datum" oder "Programm"? */ )
{ java.lang.System.out.println( string.length() ); }
public static void main( final java.lang.String[] args )
{ Main.printLength( "abc" ); }}
java.lang.System.out
3
Referenzparameter und Rekursion *
Von den elementaren Typen her ist man es vielleicht gewohnt, daß die Werte der Parameter jedes Rekursionsschrittes Kopien der Werte der Argumente sind. Dadurch wird der vorige Zustand bewahrt, er kann in der neuen Rekursionstiefe nicht verändert werden, und eine Rückkehrt zur vorigen Rekursionstiefe bedeutet die Wiederherstellung des vorherigen Zustandes.
Bei Referenztypen wird der Zustand eines Objektes bei der Übergabe an Parameter aber nicht mehr kopiert, es wird nur noch die Referenz kopiert. Daher ist es bei rekursiven Algorithmen in Java oft nötig, den Zustand am Anfang eines Rekursionsschrittes gegebenenfalls manuell zu kopieren und am Ende eines Rekursionsschrittes den vorherigen Zustand manuell wiederherzustellen.
? Übungsfrage
Was ist die Ausgabe des folgenden Programms?
Main.java
public final class Main
{public static void m( java.lang.StringBuilder v )
{ v.replace( 0, java.lang.Integer.MAX_VALUE, "gamma" );
java.lang.System.out.println( v );
v = new java.lang.StringBuilder( "delta" );
java.lang.System.out.println( v );
v.replace( 0, java.lang.Integer.MAX_VALUE, "epsilon" );
java.lang.System.out.println( v ); }public static void main( final java.lang.String[] args )
{ java.lang.StringBuilder v = new java.lang.StringBuilder( "alpha" );
java.lang.System.out.println( v );
m( v );
java.lang.System.out.println( v ); }}
? Übungsfrage
Was ist die Ausgabe des folgenden Programms?
Main.java
public final class Main
{public static void m( double x, final java.awt.geom.Point2D.Double p )
{ x = 2; p.x = 2; }public static void main( final java.lang.String[] args )
{ final double x = 0.0;
final java.awt.geom.Point2D.Double p = new java.awt.geom.Point2D.Double( 0.0, 0.0 );
m( x, p );
java.lang.System.out.println( x );
java.lang.System.out.println( p.x ); }}
Übungsaufgaben
- Ermittlung der Länge eines Strings
- Schreiben Sie eine Methode, welche die Länge eines ihr als Argumentwert übergebenen String-Objekts zurückgibt.
- Zusatzaufgabe: Schreiben Sie die Methode so, daß auch übergebene StringBuilder-Objekte akzeptiert werden.