Lesart: quadrat ist eine Funktion, die ein int als Eingabe nimmt und ein int zurückgibt. Sie multipliziert die Eingabe mit sich selbst.
Was beim Aufruf passiert (Stack-Frame Schritt für Schritt)
int ergebnis = quadrat(5); läuft intern in fünf Schritten ab und genau diese Sequenz solltest du in der Klausur traceln können:
- Argument auswerten: der Ausdruck
5 wird zu 5 ausgewertet.
- Stack-Frame anlegen: die JVM (oder Python-VM) legt einen neuen Frame auf dem Call-Stack an. Im Frame leben Parameter und lokale Variablen der Funktion.
- Parameter kopieren: der Wert
5 wird in den Parameter x kopiert. Im Frame steht jetzt x = 5.
- Body ausführen:
return x * x rechnet 5 * 5 = 25 und liefert den Wert an den Aufrufer.
- Frame zerstören, Rückgabewert zuweisen: der Frame wird vom Stack genommen,
ergebnis bekommt 25. Der Parameter x existiert nicht mehr.
Im nachfolgenden Interaktiv-Abschnitt ist genau dieses Verhalten als Code-Stepper aufbereitet (verdoppeln, mutateList, reassignList, absolutBetrag) inklusive sichtbarem Call-Stack.
Scope: Parameter und lokale Variablen sind lokal
Scope bedeutet: in welchem Bereich des Programms ein Name sichtbar/gültig ist.
- Parameter sind nur innerhalb der Funktion sichtbar. Ein Name wie
x in der Funktion und ein x außerhalb sind zwei verschiedene Variablen.
- Lokale Variablen (im Body deklariert) existieren nur bis zum Funktionsende, danach werden sie zusammen mit dem Frame zerstört.
- Java: Scope endet mit dem nächsten
}. Innerhalb eines Methoden-Blocks gilt Block-Scope.
- Python: Scope ist immer die ganze Funktion (kein Block-Scope für
if/for innerhalb einer Funktion). Funktionen, Klassen, Module und Comprehensions haben jeweils eigene Scope-Regeln. Klausur-Falle: in Python ist for i in range(...): deklariert kein Block-lokales i, das i existiert nach der Schleife weiter.
public static int verdoppeln(int x) {
return x * 2;
}
int x = 5;
int y = verdoppeln(x); // x außen bleibt 5, das x in verdoppeln ist eine Kopie
Mehrere return-Pfade und Guard-Clauses
Eine Funktion kann mehrere return-Anweisungen haben. Sobald eines erreicht wird, endet die Funktion sofort. In gut lesbarem Code werden mehrere returns gezielt eingesetzt (Guard-Clauses, klare Fallunterscheidungen), nicht beliebig verstreut. In Klausuren ist das Tracing wichtig: welcher Pfad wird genommen, was wird zurückgegeben?
public static int absolutBetrag(int x) {
if (x < 0) return -x; // Guard für negative Werte
return x; // Happy Path
}
Ein Guard-Clause (Wächter-Anweisung) prüft am Anfang einer Funktion einen Sonderfall und kehrt früh zurück. Vorteile: spart Verschachtelung, der Happy-Path bleibt eingerückt-frei und lesbar.
Warum Funktionen?
- Wiederverwendung: einmal schreiben, oft benutzen.
- Lesbarkeit:
berechneFlaeche(5, 3) ist klarer als 5 * 3.
- Fehler isolieren: wenn die Berechnung falsch ist, musst du sie nur an einer Stelle fixen.
- Testbarkeit: kleine, fokussierte Funktionen lassen sich isoliert testen.
void: Funktionen ohne Rückgabe
Funktionen müssen nicht immer etwas zurückgeben.
- Java:
void heißt kein Rückgabewert. Du kannst mit return; (ohne Wert) die Methode vorzeitig verlassen, aber niemals return wert;.
- Python: ohne explizites
return wird automatisch None zurückgegeben, das ist ein echter Wert, nicht exakt dasselbe wie Java-void.
Default-Parameter: Python ja, Java nein
Python erlaubt Default-Werte für Parameter direkt in der Signatur, Java nicht. Wer in Java optionale Parameter braucht, simuliert das per Overloading (mehrere Methoden mit gleichem Namen, unterschiedlicher Parameter-Liste).
def greet(name="Gast"):
print(f"Hallo {name}")
greet() # Hallo Gast
greet("Anna") # Hallo Anna
public static void greet() {
greet("Gast");
}
public static void greet(String name) {
System.out.println("Hallo " + name);
}
greet(); // Hallo Gast
greet("Anna"); // Hallo Anna
Klausur-Klassiker: gegeben Java-Code mit überladenen Methoden, entscheide welche Variante bei welchem Aufruf greift.
Side-Effects: was eine Funktion "nebenher" tut
Ein Side-Effect im engeren Sinn ist jede externe Wirkung einer Funktion: etwas in die Konsole drucken, eine Datei schreiben, einen Netzwerk-Call machen, eine globale Variable oder ein übergebenes Objekt mutieren. Daneben gibt es noch Abhängigkeiten von externem oder nicht-deterministischem Zustand: das Datum/die Uhrzeit lesen, Zufallszahlen ziehen, globale Variablen lesen. Diese schreiben nichts nach außen, machen die Funktion aber trotzdem nicht-deterministisch und damit nicht rein.
Beispiele:
System.out.println("...") ist ein Side-Effect (schreibt in die Konsole).
liste.add(x) ist ein Side-Effect am übergebenen Objekt.
Math.random() liest internen Zufalls-State → nicht-deterministisch, dadurch nicht rein.
LocalDateTime.now() liest externen Zeit-State → nicht-deterministisch.
Side-Effects sind nicht "böse", aber sie machen Funktionen schwerer zu testen und zu verstehen. Ein gutes Programm trennt rechnende Funktionen (ohne Side-Effects, ohne externe Abhängigkeiten) von Funktionen, die etwas tun (mit Side-Effects).
Pure Functions
Eine pure function (reine Funktion) erfüllt zwei Eigenschaften:
- Determinismus: gleicher Input ergibt immer gleichen Output.
- Keine Side-Effects: keine externen Wirkungen, keine Abhängigkeit von veränderlichem externem Zustand.
quadrat(5) liefert immer 25 und ändert nichts → pure. System.out.println("Hallo") liefert nichts (void) und ändert externen State (Konsole) → nicht pure. Pure Functions sind besonders einfach zu testen und können in geeigneten Fällen sicher memoisiert oder gecacht werden, weil gleiche Eingaben immer gleiche Ausgaben liefern. Klausur-Klassiker: gegeben eine Funktion, entscheide ob sie pure ist.