Java Method Overloading
Mehrere Methoden mit gleichem Namen, aber unterschiedlichen Parameterlisten in Java definieren, um flexible APIs zu erstellen.
Method Overloading bedeutet, zwei oder mehr Methoden in derselben Klasse mit dem gleichen Namen, aber unterschiedlichen Parameterlisten zu deklarieren. Der Compiler entscheidet anhand der übergebenen Argumenttypen, welche Methode aufgerufen wird.
Sie haben bereits überladene Methoden verwendet, ohne es zu merken. System.out.println(...) hat Versionen, die einen int, einen double, einen String, ein Object, ein char[] und weitere Typen entgegennehmen. Sie verhalten sich alle ähnlich — geben den Wert und einen Zeilenumbruch aus — aber die Implementierung unterscheidet sich je nach Typ.
Was „unterschiedliche Parameterliste" bedeutet
Zwei Überladungen müssen sich in der Stelligkeit (Anzahl der Parameter) oder in den Typen dieser Parameter in der Reihenfolge unterscheiden. Sie dürfen sich ausschließlich unterscheiden in:
- Parameternamen
- Rückgabetyp
- Ob Parameter
finalsind
public static int square(int n) { return n * n; }
// VALID overloads — different parameter types
public static double square(double n) { return n * n; }
public static long square(long n) { return n * n; }
// VALID overload — different arity
public static int sum(int a, int b) { return a + b; }
public static int sum(int a, int b, int c) { return a + b + c; }
// INVALID — only the return type differs
// public static long square(int n) { return (long) n * n; } // won't compileWie Java eine Überladung auswählt
Wenn Sie square(3) schreiben, geht der Compiler in dieser Reihenfolge vor:
- Exakte Übereinstimmung. Gibt es eine Überladung, deren Parameter genau mit den Argumenttypen übereinstimmen?
square(3)→square(int). Fertig. - Erweiterung (Widening). Falls nicht: Können die Argumente erweitert werden (z. B.
int → long,int → double)? Es wird die Überladung gewählt, die die kleinste Erweiterung erfordert. - Autoboxing / Unboxing. Andernfalls wird versucht, den Wert zu verpacken oder zu entpacken (
int ↔ Integer). - Varargs. Als letzter Ausweg wird auf eine Varargs-Überladung zurückgegriffen (siehe das Varargs-Kapitel).
Wenn zwei Überladungen auf derselben Stufe gleichwertig sind, ist der Aufruf mehrdeutig und lässt sich nicht kompilieren.
public static void show(int n) { System.out.println("int: " + n); }
public static void show(long n) { System.out.println("long: " + n); }
public static void show(double n) { System.out.println("double: " + n); }
show(3); // exact match → int
show(3L); // exact match → long
show(3.0); // exact match → double
show((short) 3); // widens short → int (closest), picks show(int)Mehrdeutigkeit
Wenn der Compiler nicht zwischen zwei gleich gut passenden Überladungen entscheiden kann, erhalten Sie einen Fehler:
public static void f(int a, long b) { /* ... */ }
public static void f(long a, int b) { /* ... */ }
f(1, 2); // ERROR: reference to f is ambiguousBeide Überladungen erfordern die Erweiterung eines Arguments von int auf long. Keine ist „besser". Die Lösung besteht darin, an der Aufrufstelle durch eine explizite Typumwandlung zu disambiguieren — f(1, 2L) oder f(1L, 2) — oder eine dritte Überladung f(int, int) hinzuzufügen, die den Fall genau abdeckt.
Überladung mit Objekttypen
Referenztypen werden auf dieselbe Weise überladen, jedoch mit Subtyp-Beziehungen anstelle von Erweiterungen:
public static void log(Object o) { System.out.println("Object: " + o); }
public static void log(String s) { System.out.println("String: " + s); }
log("hello"); // exact match → String
log(42); // autobox to Integer, then Integer is-a Object → log(Object)
log((Object) "hi"); // forces the Object overloadWenn Sie einen String übergeben und die einzige Überladung Object akzeptiert, ist Java damit einverstanden — String ist ein Object. Gibt es jedoch eine spezifischere Überladung, bevorzugt Java diese.
Wann Overloading hilfreich ist
Der Sinn des Overloadings besteht darin, den Aufrufenden eine übersichtliche, typgerechte API zu bieten. Zwei Muster treten am häufigsten auf:
Standardwerte. Eine kurze Überladung ruft die lange mit sinnvollen Standardwerten auf:
public static void greet(String name) {
greet(name, 1); // delegate
}
public static void greet(String name, int times) {
for (int i = 0; i < times; i++) {
System.out.println("Hello, " + name);
}
}Komfortkonverter. Unterschiedliche Eingabetypen, dieselbe logische Operation:
public static int lengthOf(String s) { return s == null ? 0 : s.length(); }
public static int lengthOf(int[] xs) { return xs == null ? 0 : xs.length; }
public static int lengthOf(int n) { return Integer.toString(n).length(); }Der Aufrufer schreibt lengthOf(x), ohne nachzudenken; der Compiler leitet an den richtigen Methodenrumpf weiter.
Wann man nicht überladen sollte
Wenn zwei Überladungen wesentlich unterschiedliche Dinge tun würden, sollten ihnen stattdessen unterschiedliche Namen gegeben werden. Der Leser von format(x, y) sollte nicht nachschlagen müssen, welche Überladung gewählt wurde, um zu verstehen, was der Aufruf bedeutet. Überladungen sollten Varianten derselben Idee sein, keine unterschiedlichen Ideen, die einen Namen teilen.
Overloading vs. Overriding
Diese beiden Begriffe klingen ähnlich, lösen aber unterschiedliche Probleme — ihre Verwechslung ist die häufigste Fehlerquelle.
- Overloading bedeutet gleicher Name, unterschiedliche Parameterlisten, innerhalb einer Klasse. Welche Methode ausgeführt wird, entscheidet der Compiler anhand der statischen (deklarierten) Typen der Argumente. Dies nennt man statische (Compile-Zeit-)Bindung.
- Overriding bedeutet gleicher Name, gleiche Parameterliste, in einer Unterklasse — das Ersetzen einer geerbten Methode. Welche Methode ausgeführt wird, entscheidet die JVM zur Laufzeit anhand des tatsächlichen Typs des Objekts. Dies ist dynamische (Laufzeit-)Bindung.
Object x = "hello";
log(x); // calls log(Object), NOT log(String) — chosen from x's DECLARED typeObwohl x zur Laufzeit einen String enthält, sieht der Compiler nur Object x, wählt also die Object-Überladung. Die Auswahl der Überladung berücksichtigt niemals den Laufzeittyp — das ist die Aufgabe des Overridings. Siehe Method Overriding für die Laufzeitseite des Themas.
Ein ausgearbeitetes Beispiel
Was kommt als Nächstes
Overloading ermöglicht es, einen Namen auf mehrere Methoden zeigen zu lassen. Manchmal möchte man, dass eine einzelne Methode sich selbst aufruft — ein Problem löst, indem ein Teil davon abgetrennt und eine kleinere Version desselben Problems damit bearbeitet wird. Das nennt sich Rekursion.