Spis treści
W poprzednich rozdziałach zapoznaliśmy się z podstawami tworzenia metod. W tym rozdziale nauczymy się, czym jest przeładowywanie metod.
Spójrzmy na poniższy przykład – który z komunikatów zobaczymy na ekranie?
public class DwieMetodyTaSamaNazwa { public static void main(String[] args) { wypisz(100); } public static void wypisz(int liczba) { System.out.println("Przeslana liczba: " + liczba); } public static void wypisz(int wartosc) { System.out.println("Wartosc wynosi: " + wartosc); } }
Komunikat, który zobaczymy, to komunikat z błędem kompilacji od kompilatora:
Stało się tak dlatego, że kompilator nie wie, którą z dwóch metod wypisz miałyby wywołać i nie pozwala w ogóle na skompilowanie powyższego kodu. Zauważmy także, że różnica w nazwach argumentów obu metod wypisz nie ma znaczenia.
A gdybyśmy chcieli napisać metodę wypisz wypisującą liczbę całkowitą, rzeczywistą, przesłanego Stringa czy też tablicę, to czy jesteśmy skazani na napisanie metod o różnych nazwach, jak poniżej?
public static void wypiszCalkowita(int liczba) { ... } public static void wypiszRzeczywista(double liczba) { ... } public static void wypiszString(String tekst) { ... } public static void wypiszTablice(int[] tablica) { ... }
Na szczęście nie – w języku Java (a także innych językach, jak np. C#) mamy możliwość skorzystania z mechanizmu nazywanego przeładowywaniem metod (method overloading). Pozwala on na tworzenie metod o tej samej nazwie, o ile:
- różnią się one liczbą argumentów i / lub
- argumenty różnią się typem.
Oznacza to, że możemy mieć wiele wersji funkcji wypisz, o ile ich argumenty będą się różnić:
public class PrzeladowanieWypisz { public static void main(String[] args) { wypisz(10); wypisz(7, 128); wypisz(3.14); wypisz("Witaj!"); wypisz(new int[] {2, 40, 500}); } public static void wypisz(int liczba) { System.out.println("Liczba calkowita: " + liczba); } public static void wypisz(int pierwsza, int druga) { System.out.println("Pierwsza liczba: " + pierwsza + ", druga liczba: " + druga); } public static void wypisz(double liczba) { System.out.println("Liczba rzeczywista: " + liczba); } public static void wypisz(String tekst) { System.out.println("Tekst: " + tekst); } public static void wypisz(int[] tablica) { for (int i = 0; i < tablica.length; i++) { System.out.println("Element tablica nr " + i + " to: " + tablica[i]); } } }
W powyższym programie zdefiniowaliśmy wiele wersji metody wypisz. Różnią się one liczbą i/lub typami argumentów, więc kompilator nie ma problemów z dopasowaniem wywołania odpowiedniej metody wypisz w metodzie main, więc na ekranie zobaczymy:
Kolejność argumentów także ma znaczenie – metoda, która przyjmuje liczbę i String to inna metoda, niż metoda przyjmująca String i liczbę:
public class PrzeladowanieKolejnoscArgumentow { public static void main(String[] args) { wypisz("Liczba wynosi: ", 8); wypisz(32, "2 do potegi 5 wynosi"); } public static void wypisz(String komunikat, int liczba) { System.out.println(komunikat + liczba); } public static void wypisz(int liczba, String komunikat) { System.out.println(komunikat); System.out.println(liczba); } }
Powyższy kod wykonuje się bez problemów – na ekranie zobaczymy:
Kompilator na podstawie liczby argumentów, ich typów, oraz kolejności, jest w stanie jednoznacznie określić, która z dwóch metod wypisz ma zostać użyta – najpierw wywołana zostanie metoda wypisz, która przyjmuje liczbę jako drugi argument, bo pierwsze wywołanie w metodzie main:
wypisz("Liczba wynosi: ", 8);
ma liczbę właśnie jako drugi argument. Natomiast drugie wywołanie metody wypisz:
wypisz(32, "2 do potegi 5 wynosi");
podaję liczbę jako pierwszy argument, więc użyta zostanie metoda wypisz, która oczekuje liczby jako pierwszy argument.
Typ zwracany przez metodę a przeładowywanie metod¶
Może się tutaj jeszcze nasunąć pytanie: a co ze zwracanym typem? Czy możemy mieć dwie metody o takich samych parametrach, które będą różniły się zwracanym typem?
Nie możemy – zwracany typ nie ma znaczenia – jeżeli spróbowalibyśmy utworzyć takie metody, kompilator ponownie by zaprotestował. Dla kompilatora informacja o zwracanym typie jest nieistotna przy podejmowania decyzji, którą metodę wywołać – przecież możemy całkowicie pominąć zwracaną wartość! Kompilator w takim przypadku nie wiedziałby, którą wersję metody ma użyć, a kompilacja zakończyłaby się błędem:
public class PrzeladowanieZwracanyTyp { public static void main(String[] args) { int x = podziel(100, 9); } public static int podziel(int a, int b) { return a / b; } public static double podziel(int a, int b) { return a / b; } }
Ponownie zobaczymy błąd kompilacji informujący o konflikcie nazw:
Pomimo, że metody różnią się zwracanym typem, a podczas wywoływania jednej z nich wynik przypisujemy do zmiennej typu int (a wartość tego typu zwraca tylko jedna z dwóch metod podziel), kompilator protestuje, ponieważ różnica tylko w zwracanym typie nie jest dla kompilatora wystarczająca.
Nazwy parametrów a przeładowywanie metod¶
A czy nazwy parametrów mają znaczenie? Jak już wspomnieliśmy na początku tego rozdziału, nazwy argumentów (tak jak zwracany typ) nie mają znaczenia dla kompilatora i nie są wystarczające, by móc poprawnie przeładować metodę.
Podsumowanie przeładowywania metod¶
- W języku Java mamy możliwość skorzystania z mechanizmu nazywanego przeładowywaniem metod (method overloading).
- Przeładowywanie metod pozwala na tworzenie metod o tej samej nazwie, o ile:
- różnią się one liczbą argumentów i / lub
- argumenty różnią się typem.
- Oznacza to, że możemy mieć wiele wersji tej samej metody, dostosowanych do różnych wymagań:
public static void wypisz(int liczba) { System.out.println("Liczba calkowita: " + liczba); } public static void wypisz(int pierwsza, int druga) { System.out.println("Pierwsza liczba: " + pierwsza + ", druga liczba: " + druga); } public static void wypisz(String tekst) { System.out.println("Tekst: " + tekst); }
- Kolejność argumentów także ma znaczenie – metoda, która przyjmuje liczbę i string to inna metoda, niż metoda przyjmująca string i liczbę.
- Z kolei zwracany typ nie ma znaczenia – jeżeli spróbowalibyśmy utworzyć metody o takich samych argumentach, ale innych zwracanych typach, to kompilator by zaprotestował – kompilacja zakończyłaby się błędem.
- Dla kompilatora informacja o zwracanym typie jest nieistotna przy podejmowania decyzji, którą metodę wywołać.
- Nazwy parametrów, podobnie jak zwracany typ, nie mają znaczenia i są niewystarczające, by rozróżnić metody o tych samych nazwach i takich samych argumentach.
Pytania do przeładowywania metod¶
- Czym jest przeładowywanie metod?
- Które z poniższych par sygnatur przeładowanych metod są poprawne, a które nie? Wyjaśnij, dlaczego.
public static void metoda(int liczba) public static int metoda(int liczba)
public static void metoda(int liczba, int drugaLiczba) public static void metoda(int liczba)
public static void metoda(int liczba) public static void metoda(int liczba)
public static void metoda(double liczba) public static void metoda(int liczba)
public static void metoda(double liczba, String tekst) public static void metoda(String tekst, double liczba)
public static void metoda(String tekst, double liczba) public static void metoda(String komunikat, double wartosc)
public static void metoda() public static void metoda(String komunikat)
public static void metoda(String komunikat) public static void Metoda(String komunikat)
- Które z poniższych ma znaczenie podczas przeładowywania metod i pozwoli na utworzenie metod o tej samej nazwie?
- typ zwracanej przez metodę wartości,
- liczba argumentów,
- typy argumentów,
- kolejność argumentów,
- nazwy argumentów.
Zadania do przeładowywania metod¶
Metoda porównująca swoje argumenty¶
Napisz metodę, która porównuje dwa przesłane do niej argumenty tego samego typu. Jeżeli wartości tych argumentów są sobie równe, metoda powinna zwrócić wartość true, a w przeciwnym razie false.
Metoda powinna mieć kilka wersji i przyjmować argumenty typów:
- int
- double
- boolean
- char
- String
- tablice wartości typu int: int[]
- tablice wartości typu String: String[]