Methoden¶
Bis jetzt haben wir unseren Programmcode immer in genau eine Methode geschrieben (z.B. printObjectState()
. Das wird auf Dauer viel zu unübersichtlich. Außerdem verstoßen wir so gegen zwei wichtige Prinzipien der Programmierung:
- dem Single Responsibility Principle (SRP) und
- Don't repeat yourself (DRY).
Die ursprüngliche Formulierung des SRP stammt von Robert C. Martin. Wir können uns als wesentliche Prinzipien schonmal merken, dass
- eine Variable genau eine Bedeutung haben soll und niemals für verschiedene Bedeutungen benutzt werden sollte (zwei Bedeutungen: zwei Variablen) und
- eine Methode genau eine Sache erledigen sollte.
Zunächst schauen wir uns an, was eine Methode überhaupt ist und wie wir sie definieren und verwenden. Angenommen, wir haben eine Methode in der folgenden Form:
public class Methods
{
public void myMethod()
{
int summand1 = 3;
int summand2 = 4;
int summe = summand1 + summand2;
System.out.println(summand1 + " + " + summand2 + " = " + summe); // 3 + 4 = 7
summand1 = 5;
summand2 = 9;
summe = summand1 + summand2;
System.out.println(summand1 + " + " + summand2 + " = " + summe); // 5 + 9 = 14
summand1 = -115;
summand2 = 999;
summe = summand1 + summand2;
System.out.println(summand1 + " + " + summand2 + " = " + summe); // -115 + 999 = 884
}
}
In dieser myMethod()
-Methode machen wir drei Mal das Gleiche, wir addieren 2 Summanden und geben das Ergebnis der Berechnung aus. Wir sehen insbesondere doppelten (sogar dreifachen) Code, d.h. wir wiederholen uns. Außerdem geben die vergebenen Namen nur an, wofür die Variablen da sind, aber es gibt keine namentliche Beschreibung von dem, WAS wir tun.
Methodendefinition¶
Das wollen wir ändern und lagern den sich wiederholenden Code in eine Methode aus. Diese Methode nennen wir add()
:
1 2 3 4 5 |
|
Betrachten wir diese Definition einer Methode genauer:
- In Zeile
1
sehen wir den Methodenkopf:- Das Schlüsselwort
public
besagt, dass diese Methode von allen anderen Klassen (die wir noch nicht haben) aufgerufen werden kann. Es handelt sich um eine öffentliche Methode. Wir gehen darauf genauer ein, wenn wir uns mit Sichtbarkeitsmodifizierern beschäftigen. - Das Schlüsselwort
void
steht dafür, dass der Aufruf unserer Methode keinen Wert hat, d.h. der Aufruf dieser Methode ist eine Anweisung ohne Nebeneffekt. Wenn die Methode einen Wert haben soll, dann wird hier ein Datentyp eingetragen (sehen wir im nächsten Beispiel). add
ist der Methodenname. Hier gelten die Bedingungen, die wir an Bezeichner in Java haben. Methodennamen beginnen stets mit einem Kleinbuchstaben.- Nach dem Methodennamen kommen runde Klammern und darin sogenannte Parameter. Parameter sind Variablen. Parameter werden in der Methodendefinition deklariert, aber nicht initialisiert. Parameter werden beim Aufruf der Methode initialisiert.
- Das Schlüsselwort
- In den Zeilen
2-5
steht der Methodenkörper:- Der Methodenkörper ist ein Anweisungsblock. Er beginnt mit einer öffnenden geschweiften Klammer
{
(Zeile2
) und endet mit einer schließenden geschweiften Klammer}
(Zeile5
). Innerhalb dieses Anweisungsblocks können beliebig viele Anweisungen stehen. - In Zeile
3
wird unter Verwendung der Werte der Variablen (Parameter)summand1
undsummand2
eine Summe gebildet und in der Variablensumme
vom Typint
gespeichert. - Die Werte der Parameter und der Summe werden in Zeile
4
geeignet auf die Konsole ausgegeben.
- Der Methodenkörper ist ein Anweisungsblock. Er beginnt mit einer öffnenden geschweiften Klammer
Die Definition einer Methode erfolgt immer
- innerhalb einer Klasse und
- außerhalb jeder anderen Methode.
Methodenaufruf¶
In der myMethod()
-Methode wird unsere Methode nun aufgerufen. Wichtig ist es zu beachten, dass
- wir exakt den gleichen Namen für die Methode verwenden, wie in der Methodendefinition angegeben (Groß- und Kleinschreibung beachten!) und
- dass der Methode in den runden Klammern Werte für die Parameter übergeben werden. Dabei müssen
- die Anzahl der Parameter und
- der jeweilige Typ der Parameter mit dem Aufruf übereinstimmen.
Hier nochmal die gesamte Klasse Methods
mit den Aufrufen der add()
-Methode in myMethod()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
In der myMethod()
-Methode wird nun drei Mal unsere neue add()
-Methode aufgerufen. Bei jedem Aufruf werden Werte für die Parameter übergeben. Der Aufruf der Methode entspricht einer Anweisung (Semikolon am Ende). Der Aufruf der add()
-Methode entspricht keinem Ausdruck, da der Aufruf dieser Methode ohne Wert ist. Dies liegt daran, dass in der Methodendefinition angegeben wurde, dass der Wert der Methode void
ist - also kein Wert, kein Typ.
Beachten Sie, dass in der Klasse Methods
nun zwei Methoden definiert sind, myMethod()
und add()
. Wenn wir die myMethod()
-Methode aufrufen, wird sie ausgeführt. Das bedeutet, dass alle Anweisungen, die in der myMethod()
-Methode definiert wurden, sequentiell abgearbeitet werden. Es wird also nacheinander drei Mal die add()
-Methode aufgerufen und ausgeführt. Damit die add()
-Methode überhaupt ausgeführt wird, muss sie aufgerufen werden.
Beachte
Wird add()
nirgends aufgerufen, wird sie auch niemals ausgeführt. Die Definition der Methode allein sorgt noch nicht für dessen Ausführung!
Ausführung des Programms im Detail¶
Wir schauen uns die Ausführung des obigen Programms nochmal im Detail an, um die Aufrufe genauer zu analysieren:
- Wir rufen die
myMethod()
-Methode auf (Zeile9
). - Die erste Anweisung in der
myMethod()
-Methode istadd(3,4);
(Zeile11
). - Dadurch wird die
add()
-Methode aufgerufen (Zeile3
). - Durch den Aufruf werden die Parameter der Methode deklariert und initialisiert, d.h.
int summand1 = 3
undint summand2 = 4
(Zeile3
). - Die erste Anweisung in der Methode
add()
istint summe = summand1 + summand2;
. dadurch wird die Variablesumme
deklariert und bekommt den Wert des Ausdruckssummand1 + summand2
initial zugewiesen. Dieser Wert ist7
. (Zeile5
). - Es wird die Methode
System.out.println()
aufgerufen. Der auszugebene String ergibt sich aussummand1 + " + " + summand2 + " = " + summe
. Der Wert (vom TypString
) dieses Ausdrucks ergibt sich aus:summand1 + " + "
ist ein Konkatenation; das Ergebnis ist"3 + "
."3 + " + summand2
ist ebenfalls eine Konkatenation; das Ergebnis ist"3 + 4"
."3 + 4" + " = "
ist ebenfalls eine Konkatenation; das Ergebnis ist"3 + 4 = "
."3 + 4 = " + summe
ist ebenfalls eine Konkatenation; das Ergebnis ist"3 + 4 = 7"
.
- Nach Ausgabe des Strings in Zeile
6
ist die Abarbeitung deradd()
-Methode beendet. Diese Methode wird verlassen und es wird zurück zurmyMethod()
-Methode gegangen. - Die nächste Anweisung in der
myMethod()
-Methode istadd(5,9);
(Zeile12
). - Dadurch wird erneut die
add()
-Methode aufgerufen (Zeile3
). - Durch den Aufruf werden die Parameter der Methode deklariert und initialisiert, d.h.
int summand1 = 5
undint summand2 = 9
(Zeile3
). - Die erste Anweisung in der Methode
add()
istint summe = summand1 + summand2;
. dadurch wird die Variablesumme
deklariert und bekommt den Wert des Ausdruckssummand1 + summand2
initial zugewiesen. Dieser Wert ist14
. (Zeile5
). - Es wird die Methode
System.out.println()
aufgerufen. Der auszugebene String ergibt sich aussummand1 + " + " + summand2 + " = " + summe
. Der Wert (vom TypString
) dieses Ausdrucks ist"5 + 9 = 14"
. - Nach Ausgabe des Strings in Zeile
6
ist die Abarbeitung deradd()
-Methode beendet. Diese Methode wird verlassen und es wird zurück zurmyMethod()
-Methode gegangen. - Die nächste Anweisung in der
myMethod()
-Methode istadd(-115,999);
(Zeile13
). - Dadurch wird erneut die
add()
-Methode aufgerufen (Zeile3
). - Durch den Aufruf werden die Parameter der Methode deklariert und initialisiert, d.h.
int summand1 = -115
undint summand2 = 999
(Zeile3
). - Die erste Anweisung in der Methode
add()
istint summe = summand1 + summand2;
. dadurch wird die Variablesumme
deklariert und bekommt den Wert des Ausdruckssummand1 + summand2
initial zugewiesen. Dieser Wert ist884
. (Zeile5
). - Es wird die Methode
System.out.println()
aufgerufen. Der auszugebene String ergibt sich aussummand1 + " + " + summand2 + " = " + summe
. Der Wert (vom TypString
) dieses Ausdrucks ist"-115 + 999 = 884"
. - Nach Ausgabe des Strings in Zeile
6
ist die Abarbeitung deradd()
-Methode beendet. Diese Methode wird verlassen und es wird zurück zurmyMethod()
-Methode gegangen. - In der
myMethod()
-Methode gibt es keine weitere Anweisung mehr. Der Aufruf dieser Methode ist beendet.
Methode gibt einen Wert zurück¶
Unsere Methode add()
hat keinen Wert zurückgegeben. Das wurde im Methodenkopf festgelegt, wo wir mit void
definiert haben, dass der Aufruf der Methode keinem Wert entspricht. Dies ist typisch für Methoden, die etwas auf die Konsole ausgeben. Alle Methoden, deren Aufgabe es ist, etwas auszugeben, sind (sollten sein) vom Rückgabetyp1 void
.
Jetzt erstellen wir eine Methode computeSum()
, die das gleiche macht wie add()
, aber mit dem Unterschied, dass diese Methode nichts auf die Konsole ausgibt, sondern die Summe der beiden Parameter an den Aufrufer der Methode zurückgibt .
Die Definition dieser Methode sieht dann so aus:
1 2 3 4 5 |
|
Zwei ganz wesentliche Unterschiede zur Definition von add()
fallen auf:
- Diese Methode hat einen Rückgabetyp (
int
). Dort, wo beiadd()
nochvoid
stand, steht beicomputeSum()
im Methodenkopfint
. Damit wird festgelegt, dass der Aufruf der Methode einem Wert entspricht, welcher vom Typint
ist. Der Aufruf dieser Methode ist somit ein Ausdruck! - Die letzte Anweisung der Methode
computeSum()
ist eine Anweisung, die mit dem Schlüsselwortreturn
beginnt. Jede Methode, die einen Rückgabetyp hat (also genau nichtvoid
), muss ein solchesreturn
enthalten. Diesesreturn
muss die letzte Anweisung in der Methode sein und es muss einen Wert zurückgeben, der von dem Typ ist, der für die Methode als Rückgabetyp definiert wurde. Hier ist es der Wert vonsumme
.summe
ist vom Typint
und somit istreturn summe;
korrekt, da die Methode ja einint
zurückgeben soll.
Aufruf einer Methode, die einen Wert zurückgibt¶
Unsere Methode computeSum()
könnte nun in der myMethod()
-Methode wie folgt aufgerufen werden:
computeSum(3,4); // korrekt, aber sinnlos
Ein solcher Aufruf macht aber gar keinen Sinn, weil die Methode selbst ja z.B. nichts ausgibt und somit hat diese Methode gar keinen Effekt. Sinnvoll eingesetzt werden kann eine solche Methode nur als Ausdruck, z.B.:
int sum = computeSum(3,4); // sum wird mit dem Wert 7 initialisiert
System.out.println(computeSum(5,9)); // es wird 14 ausgegeben
Der Aufruf der Methode ist somit ein arithmetischer Ausdruck und kann auch als solcher behandelt werden, z.B. mit anderen arithmetischen Ausdrücken mittels arithmetischer Operatoren zu einem weiteren arithmetischen Ausdruck verknüpft werden.
Hier noch weitere Beispiele für Methoden mit Rückgabe (hier Rückgabe vom Typ boolean
):
public boolean areEqual(int nr1, int nr2)
{
return (nr1 == nr2);
}
public boolean isDivider(int nr1, int nr2)
{
return (nr1%nr2 == 0);
}
Sie können auch Methoden in Methoden aufrufen. Nehmen wir die beiden Methoden areEqual(int, int)
und isDivider(int, int)
und angenommen, wir wollen für 2 int
-Zahlen prüfen, ob die eine Teiler der anderen ist, aber beide sollen nicht gleich sein, dann können wir folgende Methode schreiben:
public boolean isDividerButNotEqual(int nr1, int nr2)
{
return (isDivider(nr1, nr2) && !areEqual(nr1, nr2));
}
Das schauen wir uns einmal genauer an:
- wir definieren wieder eine Methode wie gehabt:
- wir vergeben einen Namen (
isDividerButNotEqual
) und - wir legen fest, dass bei Aufruf der Methode zwei
int
-Werte übergeben werden müssen ((int nr1, int nr2)
). - als Rückgabetyp definieren wir
boolean
, denn wir wollen ja prüfen, ob sich die beiden ganzzahlig teilen, aber nicht gleich sind
- wir vergeben einen Namen (
- innerhalb der Methode rufen wir die Methode
isDivider(nr1, nr2)
auf und übergeben dabei unsere Werte fürnr1
undnr2
. Der Aufruf dieser Methode entspricht einem boole'schen Ausdruck, da diese Methode einboolean
zurückgibt (true
, wennnr2
Teiler vonnr1
ist undfalse
sonst - also, wenn nicht) - außerdem rufen wir die Methode
areEqual(nr1, nr2)
auf und übergeben dabei ebenfalls unsere Werte fürnr1
undnr2
. Der Aufruf dieser Methode entspricht ebenfalls einem boole'schen Ausdruck, da diese Methode einboolean
zurückgibt (true
, wennnr1
undnr2
gleich sind undfalse
sonst - also, wenn nicht) - wir wollen aber prüfen, ob sie nicht gleich sind, also schreiben wir
!areEqual(nr1, nr2)
- also die Negation dieses Ausdrucks - wir wollen prüfen,
- ob
isDivider(nr1, nr2)
UND NICHTareEqual(nr1, nr2)
, - also
isDivider(nr1, nr2)
UND!areEqual(nr1, nr2)
, - also
isDivider(nr1, nr2) && !areEqual(nr1, nr2)
- ob
- diesen Wert geben wir zurück
1. Übung Methoden mit Rückgabe
Schreiben Sie eine Methode isEven(int number)
, die ein true
zurückgibt, wenn number
gerade ist und sonst false
.
1. Übung Methoden mit Rückgabe
Schreiben Sie eine Methode isOdd(int number)
, die ein true
zurückgibt, wenn number
ungerade ist und sonst false
. Dieses Mal verwenden Sie aber die Methode isEven()
, um den richtigen Wert zu ermitteln.
Success
Wir können uns nun Methoden selber definieren. Die Definition von Methoden erfolgt innerhalb der Klasse, aber außerhalb jeder anderen Methode. Eine Methode kann entweder keinen Wert zurückgeben. Dann ist der "Rückgabetyp"1 void
. Eine solche void
-Methode gibt typischerweise etwas auf die Konsole aus. Oder die Methode gibt einen Wert zurück. Dann wird der Datentyp dieses Wertes im Methodenkopf der Methodendefinition angegeben. Die Rückgabe des Wertes erfolgt durch return
. Die return
-Anweisung muss die letzte Anweisung in der Methode sein. Der Aufruf einer solchen Methode entspricht dann einem Ausdruck.
Einer Methode können beliebig viele Parameter übergeben werden. Diese lokalen Variablen werden im Methodenkopf in den runden Klammern durch Komma getrennt deklariert. Bei Aufruf der Methode müssen diesen Variablen Werte übergeben werden (Anzahl und Datentypen müssen bei Methodenaufruf passen).