Methoden¶
Bis jetzt haben wir unseren Programmcode stets in die main()
-Methode geschrieben. 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, der es als ein Prinzip der Objektorientierung einführte und es ein wenig anders meinte, als wir es hier verwenden. Dazu kommen wir, wenn wir uns mit Objektorientierung beschäftigen. Wir können uns aber 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 ein Programm in der folgenden Form:
public class Methods
{
public static void main(String[] args)
{
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 main()
-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 laden den sich wiederholenden Code in eine Methode aus. Diese Methode nennen wir add()
:
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
static
besagt, dass wir diese Methode verwenden (aufrufen) können, ohne eine Objekt der KlasseMethods
erzeugen zu müssen. Wir können derzeit eh noch keine Objekte erzeugen, also definieren wir zunächst alle unsere Methoden alsstatic
(statisch, Klassenmethode). - 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 main()
-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 main()
:
In der main()
-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, main()
und add()
. Die main()
-Methode ist die Programmmethode, die automatisch ausgeführt wird, sobald wir das Programm starten. Damit die add()
-Methode ausgeführt wird, muss sie aufgerufen werden.
Beachte
Es werden nur alle Anweisungen ausgeführt, die in der main()
-Methode enthalten sind! Wird add()
nie in main()
aufgerufen, wird add()
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:
- durch das Starten des Programms wird die
main()
-Methode aufgerufen (Zeile9
) - die erste Anweisung in der
main()
-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 zurmain()
-Methode gegangen. - die nächste Anweisung in der
main()
-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 zurmain()
-Methode gegangen. - die nächste Anweisung in der
main()
-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 zurmain()
-Methode gegangen. - in der
main()
-Methode gibt es keine weitere Anweisung mehr. Das Programm 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:
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 main()
-Methode wie folgt aufgerufen werden:
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 static boolean areEqual(int nr1, int nr2)
{
return (nr1 == nr2);
}
public static 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 static 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 booleschen Ausdruck, da diese Methode ein
booleanzurückgibt (
true, wenn
nr2Teiler von
nr1ist und
false` 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 booleschen Ausdruck, da diese Methode ein
booleanzurückgibt (
true, wenn
nr1und
nr2gleich sind und
false` 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).