„Hallo Amigo! Vor ein paar Tagen habe ich dir vom Überladen von Methoden erzählt. Hast du alles verstanden?“

„Ja. Ich erinnere mich. Jede Klassenmethode muss eindeutig sein. Eine Member-Methode ist eindeutig, wenn die Klasse keine andere Methode mit demselben Namen und denselben Parametertypen (und die Reihenfolge der Parameter ist relevant) besitzt.“

„Sehr gut! Ich sehe schon, du hast diese Lektion gut gelernt. Heute möchte ich dir noch mehr Wissen zu diesem Thema mit auf den Weg geben. Welche Methode wird wohl in jedem Fall aufgerufen?“

Code
class Cat
{
 public static void print(int n)
 {
  System.out.println(n);
 }
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }
public static void main(String[] args)
{
  Cat.print(1);
  Cat.print((byte)1);
  Cat.print("1");
  Cat.print(null);
 }
}

„Das ist schwer zu sagen.“

„Im ersten Fall ist 1 ein int. Wir haben eine absolute Übereinstimmung mit der Methode, die ein int entgegennimmt. Also wird void print(int n) aufgerufen.

Im zweiten Fall haben wir keine Methode, die ein byte entgegennimmt. Aber es gibt zwei Methoden, die ein Short und ein Int entgegennehmen. Aufgrund der Regeln zur Typerweiterung wird ein byte zuerst zu einem short und dann zu einem int erweitert. Das Urteil lautet also, dass void print(short n) aufgerufen wird.

Im dritten Fall haben wir eine absolute Übereinstimmung mit der Methode, die ein String entgegennimmt. Also wird die Methode void print(String s) aufgerufen.

Der vierte Fall ist mehrdeutig. null hat keinen bestimmten Typ. Der Compiler wird diesen Code nicht kompilieren. In diesem Fall müssen wir Cat.print((Integer)null) schreiben, um die dritte Methode aufzurufen, und Cat.print((String)null), um die vierte Methode aufzurufen.“

„Das war sehr aufschlussreich. Vielen Dank.“

„Ich möchte dich noch darauf hinweisen, dass sich bei der Bestimmung der richtigen aufzurufenden Methode die Typen nur erweitern können. Sie können nicht eingeschränkt werden. Betrachte dieses Beispiel:“

Code
class Cat
{
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }

 public static void main(String[] args)
 {
  Cat.print((byte)1);
  Cat.print(1);
 }
}

Im ersten Fall wird der byte-Typ zu einem short erweitert und die erste Methode aufgerufen: void print(short n).

Im zweiten Fall erfolgt eine implizite erweiternde Umwandlung von int zu Integer, und dann wird die zweite Methode aufgerufen: void print(Integer n).

„Das hatte ich nicht erwartet.“

„Nein, hier ist die echte Überraschung:“

Java-Code Beschreibung
class Cat
{
 public static void print(Object o)
 {
  System.out.println(o);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }

 public static void main(String[] args)
 {
  Cat.print(1);
  Cat.print(null);
 }
}
Im ersten Fall wird int zu Integer erweitert. Da es keine Methode für Integer gibt, ist die am besten geeignete Methode (und die aufgerufene) void print(Object o).

Im zweiten Fall wird es keine Kompilierungsfehler geben und es wird void print(String s) aufgerufen, was nicht ganz offensichtlich ist.

„Amigo, ich hoffe du verstehst, dass es in solchen Fällen am besten ist, einen Typumwandlungsoperator anzugeben (wie wir es mit ‚(byte)‘ gemacht haben), damit man genau weiß, welche Methode aufgerufen wird.“

„Ich hätte nicht erwartet, dass es durch das Überladen von Methoden zu Problemen kommt. Aber dann kommst du um die Ecke. Danke, Ritschie. Ich werde in diesem Punkt die Augen weiter offen halten.“