– Cześć, Amigo! Kilka dni temu mówiłem Ci o przeciążaniu metod. Wszystko zrozumiałeś?

– Tak. Pamiętam. Każda klasa musi być unikalna. Metoda składowa jest unikalna, jeśli klasa nie ma innej metody o tej samej nazwie i tych samych typach parametrów (kolejność parametrów również ma znaczenie).

– Bardzo dobrze. Widzę, że dobrze nauczyłeś się tej lekcji. Dzisiaj chcę tylko odrobinę poszerzyć Twoją wiedzę na ten temat. Jak myślisz, jaka metoda będzie wywoływana w każdym przypadku?

Kod
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);
 }
}

– Trudno powiedzieć.

– W pierwszym przypadku 1 to typ int. Mamy 100% dopasowanie do metody, która przyjmuje typ int. Zostanie wywołana pierwsza metoda void print(int n).

W drugim przypadku nie mamy metody, która przyjmuje typ byte. Ale są dwie metody, które przyjmują typ short i int. W oparciu o zasady rozszerzania typu, byte zostanie najpierw rozszerzony do typu short, a następnie do typu int. W rezultacie wywołana zostanie metoda void print(short n).

W trzecim przypadku mamy 100% dopasowanie do metody, która przyjmuje typ String. Zostanie wywołana metoda void print(String s).

Czwarty przypadek jest niejednoznaczny. Null nie ma określonego typu. Kompilator odmówi kompilacji tego kodu. W tym przypadku musimy napisać Cat.print((Integer)null), aby wywołać trzecią metodę i Cat.print((String)null), aby wywołać metodę czwartą.

– To bardzo przydatne informacje. Dziękuję.

– Chciałbym zwrócić Twoją uwagę na to, że przy określaniu właściwej metody do wywołania typy mogą się tylko rozszerzać. Nie mogą się zawężać. Przeanalizuj ten przykład:

Kod
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);
 }
}

W pierwszym przypadku typ byte zostanie rozszerzony do typu short i zostanie wywołana pierwsza metoda: void print(short n).

W drugim przypadku nastąpi domyślne rozszerzenie konwersji z int na Integer, a następnie zostanie wywołana druga metoda: void print(Integer n).

– Tego się nie spodziewałem.

– Teraz to dopiero się zdziwisz:

Kod Java Opis
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);
 }
}
W pierwszym przypadku typ int zostanie rozszerzony do typu Integer. Ponieważ nie ma żadnej metody dla typu Integer, najbardziej odpowiednią metodą (i właśnie ona zostanie wywołana) jest metoda void print(Object o)

W drugim przypadku nie będzie żadnych błędów w kompilacji i, co nie jest do końca oczywiste, zostanie wywołana metoda void print(String s).

– Amigo, mam nadzieję, że rozumiesz to, że w takich przypadkach najlepiej jest określić operator rzutowania typów (jak to zrobiliśmy z «(byte)»), aby wiedzieć dokładnie, która metoda zostanie wywołana.

– Nigdy nie przypuszczałem, że z przeciążania metod mogą wyniknąć jakieś problemy. I zjawiłeś się Ty. Dziękuję, Raszi! Będę miał to na uwadze.