4.1 Methoden

Methoden

Methoden sind ein wichtiges Strukturierungs- und Modularisierungsmittel.

Die Grundidee einer Methode ist wiederkehrende Aufgaben an einer Stelle zu implementieren und damit Redundanz zu vermeiden.

Ein zweiter wichtiger Aspekt ist die Senkung der Komplexität. Dem Benutzer muss die Implementierung einer Methode nicht bekannt sein um sie zu benutzen. 

Der in Java verwendete Begriff wird in anderen Programmiersprachen auch wie folgt bezeichnet:

  • Prozedur
  • Unterprogramm
  • Funktion

In Java wurden alle diese Begriffe der Übersichtlichkeit wegen im Begriff der Methode zusammen gefasst. Methoden gehören immer zu Klassen. Man kann in Java keine alleinstehenden Funktionen, Prozeduren etc. implementieren

Implementierung einer Methode

Methoden bestehen aus

  • einem Methodenkopf bestehend aus der Deklaration
    • Namen
    • Signatur: Aufrufargumente, Rückgabewert
  • Methodenrumpf
    • der Rumpf wird von geschweiften Klammern eingeschlossen
    • der Rumpf enthält die Implementierung der Methode

Zum Aufruf der Methode ist nur die Kenntnis der Deklaration notwendig

Methodenkopf

Methodenkopf

Durch ihn wird die Methode deklariert. Das bedeutet, das hiermit eine Schnittstelle festgelegt wird die alle Informationen enthält die ein Benutzer der Methode kennen muss um sie zu benutzen.

public static int multiplizieren( int faktor1, int faktor2) { // Methodenrumpf }
   ^      ^    ^          ^           ^
   1.     2.   3.         4.          5.

1. Zugriffsspezifikation; die Sichtbarkeit der Methode

  • public : Die Methode darf von überall aufgerufen werden
  • private: Die Methode darf nur innerhalb der Klasse verwendet werden
  • protected: Die Methode darf nur innerhalb der Klasse oder von abgeleiteten Klassen verwendet werden

2. Statische Methoden: Das Schlüsselwort static erlaubt den Aufruf der Methode auch wenn es für die Klasse keine Instanz gibt. Das Konzept von Klassen, Objekten und Instanzen wird erst später eingeführt.

3. Rückgabetyp: Der Typ des Ergebnis welche die Methode zurückliefert. Es gibt zwei Möglichkeiten:

  • Die Methode liefert ein Ergebnis zurück. Der Typ ist ein beliebiger Typ
  • Die Methode liefert kein Ergebnis zurück. Hierzu wird das Schlüsselwort void verwendet.

4. Methodenname: Hiermit werden Methoden in einer Klasse unterschieden.

5. Parameterliste: Sie enthält den Typ und den Namen aller übergebenen Werte innerhalb von runden Klammern.

  • Die übergebenen Werte werden in der Methode als Variablen verwendet.
  • Die übergebenen Parameter werden mit Komma getrennt.
  • Sollen einer Methode keine Parameter mitgegeben werden, so wird ein leeres Klammernpaar ohne Werte verwendet. Bsp. ()
  • Die übergebenen Parametervariablen sind immer Kopien der aufrufenden Variablen. Dies bedeutet, dass man in der Methode eine Parametervariable ändern kann, ohne dass die Variable im äusseren Block die den Wert übergeben hat geändert wird.

Methodenrumpf

In Methodenrümpfe kann man:

  • Variablen und Konstanten deklarieren
  • Anweisungen und Schleifen ausführen
  • Methoden der gleichen oder anderer Klassen aufrufen
  • die Übergabeparameter nutzen

In Methoden können die im Methodenkopf deklarierten Parameter direkt verwendet werden. Bei einem gegebenen Rückgabeparameter muss dieser mit dem Schlüsselwort return zurückgegeben werden. Mit final bezeichnete Parameter dürfen im Methodenrumpf nicht mehr verändert werden. Beispiel

public static String textverkettung (String text, final int anzahl) {
   String s="";
   for (int i=0; i<anzahl; i++) {
      s = s + text;
   }
   return s; }

Die Rückgabe von Werten mit Hilfe des Schlüsselwort return funktioniert nicht nur mit Variablen, man kann auch Ausdrücke zurückgeben die den passenden Typ haben.

Wie zum Beispiel in der Methode textRahmen() die noch ein Textrahmen von jeweils 3 Zeichen um das Ergebnis legt:

public static String textRahmen (String text) {
   return ">>>" + text + "<<<";
}

Bei einem gegebenen Rückgabeparameter kann der Rumpf an verschiedenen Stellen verlassen werden.

Siehe Beispiel:

package s1.block4;
public class Textverkettung {
    public static void main(String[] args) {
        System.out.println(textverkettung("Sonnenschein", 3));
        System.out.println(textverkettung("Sonnenschein", 0));
    }
    public static String textverkettung(String text, final int anzahl) {
        String s="";
        if (anzahl < 1) {
            return "Gewählte Anzahl ist zu klein";
        } else {
            for (int i = 0; i < anzahl; i++) {
                s = s + text;
            }
            return s;
        }
    }   
}

Der Typ des Rückgabeparameter muss vom Typ her zum deklarierten Rückgabetyp der Methode passen.

Die Methode kann auch ohne einen Rückgabewert definiert werden. Im folgenden Beispiel wird sie den Text direkt selbst ausdrucken:

public class TextverkettungDrucken {
    public static void main(String[] args) {
        textverkettungDrucken("Sonnenschein", 3);
        textverkettungDrucken("Sonnenschein", 0);
    }
    public static void textverkettungDrucken(String text, final int anzahl) {
        String s="";
        if (anzahl >0)
            for (int i = 0; i < anzahl; i++)
                s = s + text;
        System.out.println("Ergebnis: " + s);
    }   
}

Aufruf von Methoden

Direkter Aufruf: Aufruf nur durch Methodenname und aktuelle Parameter

  • Die aufgerufene Methode gehört zur gleichen Klasse oder einer Oberklasse (Fall 1 im Beispiel)
  • Die Methode ist nicht statisch und wird auf das gleiche Objekt wie die umgebende Methode angewendet (Fall 1 im Beispiel)
  • Die Methode ist statisch und kann ohne die Existenz eines Objekts verwendet werden (Fall 2 im Beispiel)

Aufruf mit vorgestelltem Objekt oder Klassenname (statische Methode)

  • Die Methode soll für ein bestimmtes Objekt aufgerufen werden. Sie ist nicht statisch (Fall 3 im Beispiel)
  • Die Methode ist statisch und gehört zu einer anderen Klasse. (Fall 4 im Beispiel)

Beispiel:

public class Flugzeug {
    String kennzeichen;
    int leerGewicht;
    int besatzung; 
    public void drucken () {
        System.out.println(kennzeichen + " , Crew: " + besatzung + ", "+leerGewicht + "kg");
    }
    public static void main(String[] args) {
        Flugzeug f = new Flugzeug();
        f.drucken(); 
        f.kennzeichenAusgeben(); // Fall 3: Aufruf einer Methode für ein Objekt
        eigenschaft(); // Fall 2: Aufruf einer statischen Methode der gleichen Klasse
        Flughafen.eigenschaft(); // Fall 4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehören kann
        double pi = Math.sqrt(2.0); // Fall 4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehört
    }
    public void kennzeichenAusgeben() {
        drucken(); // Fall 1: Nicht statischer Methodenaufruf einer Methode der gleichen Klasse
    }
    public static void eigenschaft() {
        System.out.println("kann fliegen");
    }
}
 
public class Flughafen {
    public static void eigenschaft() {
        System.out.println("ist laut und schafft Arbeitsplätze");
    }
}

Anwenden von Methoden

Methoden mit Eingabeparametern können bei jedem Aufruf mit anderen Werten arbeiten. Wichtig ist hierbei zu wissen, dass es bei mehreren Parameter auf die Reihenfolge der Parameter ankommt. Sie müssen beim Aufruf, in ihrer Reihenfolge, zur Deklaration passen. Der Übersetzer wird Parameter mit nicht passenden Typen als Fehler anzeigen. Sind die Typen mehrerer Parameter gleich oder kompatibel, muss der Entwickler selbst auf die Reihenfolge achten!

Definition
Formalparameter von Methoden
Die in der Deklaration verwendeten Parameter einer Methode werden Formalparameter genannt

 

Definition
Aktualparameter von Methoden
 Die beim Aufruf einer Methoden verwendeten Parameter werden Aktualparameter (aktuelle Parameter) genannt.

 

Die oben gezeigte Methode textverkettung kann bei jedem Aufruf einen anderen Text verketten. Man kann die Methode wie folgt aufrufen.

package s1.block4;
public class Textverkettung {
    public static void main(String[] args) {
        String result;
        String text1 = "Mustertext";
        result= textverkettung("PingPong", 2);
        System.out.println("Ergebnis: " + result);
        result= textverkettung(text1, 3); 
        System.out.println("Ergebnis: " + result);
    }
    public static String textverkettung(String text, final int anzahl) {
        String s;
        s = text;
        if (anzahl < 1) {
            return "Gewählte Anzahl ist zu klein";
        } else {
            for (int i = 1; i < anzahl; i++) {
                s = s + text;
            }
            return s;
        }
    } 
}

Das oben gezeigte Codestück sollte auf der Konsole das folgende Ergebnis ausdrucken:

Ergebnis: PingPongPingPong
Ergebnis: MustertextMustertextMustertext

Der Aufruf von Methoden ohne Rückgabeparameter ist noch einfacher. Im folgenden Beispiel wird wie Methode textverketttungDrucken() aufgerufen:

... 
String result;
String text1 = "Mustertext";
textverkettungDrucken("PingPong", 2); textverkettungDrucken(text1, 3);
...

Man kann die Methode ohne eine Ergebnisvariable mit einer vereinfachten Syntax aufrufen. Ruft man Methoden mit Ergebnisvariablen in der oben gezeigten Form, ohne Zuweisung des Ergebnis zu einer Variablen, auf, so geht das Ergebnis verloren. Die Implementierung wird jedoch übersetzt und abgearbeitet. Es tritt keine Fehlermeldung auf.

Verschachtelte Methodenaufrufe und Methodenaufrufe innerhalb von Methoden

Methodenaufrufe mit Ergebnissen können überall benutzt werden wo in einem Ausdruck oder einer Zuweisung der entsprechende Typ gelesen werden soll.

Im folgenden Beispiel wird die Methode textverkettung() verschachtelt mit der Methode zur Rahmenerzeugung textRahmen() aufgerufen. Der geschachtelte Aufruf erfolgt in der Methode testTextRahmen():

public static String textverkettung(String text, final int anzahl) {
   String s="";
   if (anzahl < 1) {
      return "Gewählte Anzahl ist zu klein";
   } else {
      for (int i = 0; i < anzahl; i++) {
         s = s + text;
      }
      return s;
   }
} 
  
public static String textRahmen(String s) {
   return "<< " + s + " >>";
}    
  
public static void testTextRahmen() {
   String s = "Inhalt";
   String result = textRahmen(textverkettung(s,2));
   System.out.println(result);
}

Ein Aufruf der Methode testTextRahmen() ergibt die Ausgabe:

<< InhaltInhalt >>

Methoden können nicht nur wie eben beschrieben geschachtelt werden. Man kann innerhalb eines Methodenrumpfes auch eine andere Methode aufrufen. Dies geschah oben in der Methode testTextRahmen.

Überladen von Methoden

Java erkennt eine Methode einer Klasse an den folgenden Merkmalen:

  • Name der Methode
  • Reihenfolge und Typ der Eingabeparameter

Man kann also den gleichen Namen einer Methode wiederverwenden solange sich die Typen der Eingabeparameter unterscheiden. Hiermit kann man Methoden implementieren, die "ähnliches" implementieren. Die "Ähnlichkeit" kann man mit dem gleichen Namen dokumentieren, die Parameterlisten müssen sich aber unterscheiden.

Definition

Überladene Methoden
Überladene Methoden einer Klasse sind Methoden die den gleichen Namen besitzen und Formalparameterlisten mit unterscheidlichen Typen bzw. unterschiedlicher Anzahl von Parametern besitzen

Überladene Methoden sind nützlich wenn man den prinzipiell gleichen Algorithmus für unterschiedliche Datentypen oder unterschiedliche Parameterkombinationen ausführen will.

Wichtig: Der Rückgabeparameter einer Methode wird in Java nicht bei der Unterscheidung überladener Methoden beachtet!

Methoden mit Rückgabeparameter können auch ohne Zuweisung ihres Rückgabewertes an eine Variable aufgerufen werden.
In diesem Fall kann der Übersetzer nicht feststellen welche Methode benutzt werden soll, wenn es mehrere Methoden gibt die sich nur im Rückgabewert unterscheiden.

Ein typisches Beispiel ist das drucken von unterschiedlichen Werten. Anbei vier überladene Methoden einer Klasse;

public class drucker {

public void drucken(String s) {
   System.out.println("String:"+ s);
   }

public void drucken(int i) {
   System.out.println("Zahl: " + i);
   }

public void drucken(String s, int i) {
   System.out.println("String/Zahl" + s + "/" + i);
   }

public void drucken(int i, String s) {
   System.out.println("Anders: String/Zahl" + s + "/" + i);
   }
}

Die folgenden zwei Methoden sind keine erlaubten (zusätzlichen) überladene Methoden:

public void drucken(String z, int a) {
   System.out.println("String/Zahl" + z + "/" + a);
   }
public int drucken(String s, int i) {
   System.out.println("String/Zahl" + s + "/" + i);
   return 2*i;
   }

Diese erste Methode besitzt die gleichen Formalparametertypen wie die dritte Methode. Die Namen der Parameter sind nicht relevant.

Die zweite Methode hat zwar einen anderen Rückgabeparameter, jedoch die gleichen Formalparameter wie die dritte Methode. Java ignoriert die Rückgabeparameter und verweigert das Übersetzen der Methode.

Entwurfsprinzipien für Methoden

  • Methoden sollten eine klar definierte Teilaufgabe lösen
  • Wird eine Methode groß und unübersichtlich sollte man sie in sinnvolle Teilmethoden zerlegen
  • Gute Methoden werden an verschiedenen Stellen wieder verwendet
  • Methoden sollten möglichst gekapselt sein
    • Die Übergabeparameterlisten sollten so kurz wie möglich sein
    • Methoden sollten möglichst wenig globale Variablen verwenden. Dem Verwender ist das nicht unbedingt bewusst!
    • Ergebnisse sollten wenn möglich als Rückgabewert ausgegeben werden. Das Ändern von globalen Variablen sollte man wenn möglich vermeiden.
  • Methoden und ihre Funktionen und Seiteneffekte sollten immer gut dokumentiert sein. Ein Verhältnis von Kommentar zu Quellcode von 1:1 ist durchaus vernünftig!

Programmstrukturierung mit Hilfe von Methoden

Methoden sind einer der Bausteine die die Entwicklung komplexer Anwendungen erlauben. Sie geben Strukturierungsmöglichkeiten um die Gesamtkomplexität von Anwendungen zu reduzieren:

  • Abstraktion: Verwender einer Methode müssen nicht wissen wie sie intern funktioniert. Sie können sich auf die Beschreibung und die korrekte Ausführung der Methode verlassen
  • Codereduktion: Das wieder verwenden von Methoden erlaubt es mit deutlich weniger Programmcode auszukommen. Die Kosten für die Pflege der Quellen und die Fehlerhäufigkeit der Anwendung sinkt. Bei replizierten Codestücken müssten alle Codestücke gefunden und individuell angepasst werden.
  • Strukturierung und Hierarchisierung: Das explizite Zusammenfassen von Codestücken die eine bestimmte Aufgabe lösen erleichtert Verständnis und Pflege der Anwendung. Methoden erlauben durch Aufruf von anderen Methoden die Strukturierung in vielschichtige Hierarchien.
  • Teile und Herrsche: Das abstrahieren und Verbergen der Implementierung (Information hiding) erlaubt die Zusammenarbeit in Teams und die Entwicklung komplexer Anwendungen. Das Konzept der Schnittstelle ist ein "Vertrag" zwischen Anbieter und Konsument bei dem nur die semantische Wirkung der Methode verstanden sein muss.

Methoden sind nur das erste von mehreren Strukurierungshilfsmittel der Sprache Java die im Rahmen dieser Vorlesung vorgestellt werden. Klassen, Pakete, Vererbung und globale vs. lokale Variablen sind andere Möglichkeiten.

 

Anonymous (not verified)

Tue, 11/17/2015 - 11:02

nur eine Kleinigkeit, aber das "Überladen von Methoden", das über dem Text "Java erkennt eine Methode einer Klasse an den folgenden Merkmalen:" in Konsolenausgabe steht, sollte sicher eigentlich als neue Überschrift gedacht sein, oder?

Stefan Schneider

Wed, 11/18/2015 - 11:42

In reply to by Anonymous (not verified)

Vielen Dank,
der Fehler wurde gerichtet und der dazugehörende Abschnitt etwas umformuliert.

Anonymous (not verified)

Tue, 12/19/2017 - 18:51

Eine Methode leitet doch einen Programmblock ein. Bei der Erklärung des Methodenkopfes endet dieser jedoch mit einem Semikolon. Vielleicht kann man diesem noch mit einer geschweiften Klammer austauschen.