Spis treści
- Rozdział 7 - Metody
- Czym są metody?
- Zakres i wywoływanie metod, zmienne lokalne
- Wartości zwracane przez metody
- Argumenty metod
- Metody typu string
- Dokumentowanie metod
- Podsumowanie, pytania i zadania do argumentów metod i metod typu String
- Przeładowywanie metod
Metody mogą zwracać dowolną wartość, zarówno typu prymitywnego (jak int czy boolean), oraz złożonego (np. String lub tablica liczb rzeczywistych double[]). Metody mogą także nie zwracać żadnej wartości – wtedy, zamiast podawać zwracany typ, używamy słowa kluczowego void. Przykładem takiej metody jest każda napisana przez nas metoda main:
public static void main(String[] args) { System.out.println("Witaj Swiecie!"); }
Uwaga: zwracany typ (lub słowo kluczowe void) musi poprzedzać nazwę metody – poniższa definicja metody main jest niepoprawna i jej próba kompilacji zakończy się błędem:
// blad! void musi byc zaraz przed main! void public static main(String[] args) { // blad kompilacji System.out.println("Witaj Swiecie!"); }
Słowo kluczowe return¶
Metody zwracają wartość poprzez użycie słowa kluczowego return. Użycie słowa kluczowego return natychmiast przerywa metodę i zwraca wartość. Metoda może zawierać więcej niż jedno użycie return, ponieważ możemy uzależnić zwracaną wartość od pewnego warunku:
public class ZwrocWiekszaLiczbe { public static void main(String[] args) { int x = ktoraWieksza(100, 200); int y = ktoraWieksza(-5, -20); System.out.println("x wynosi " + x); // wypisze 200 System.out.println("y wynosi " + y); // wypisze -5 } public static int ktoraWieksza(int a, int b) { if (a > b) { return a; } else { return b; } } }
Zdefiniowana powyżej metoda ktoraWieksza zwraca większą z dwóch liczb. Słowo kluczowe return użyte zostało dwa razy – w zależności od tego, która z liczb jest większa, metoda zwróci albo wartość zapisaną w argumencie a albo b.
Metody, które nie zwracają wartości (tzn. definiują zwracany typ jako void), także mogą zawierać użycie słowa kluczowego return, ale musi po nim od razu następować średnik – może być to przydatne, gdy chcemy zakończyć działanie metody przed wykonaniem wszystkich operacji (np. na podstawie jakiegoś warunku):
public class WypiszWynikDzielenia { public static void main(String[] args) { wypiszWynikDzielenia(10, 0); wypiszWynikDzielenia(25, 5); } public static void wypiszWynikDzielenia(int x, int y) { if (y == 0) { System.out.println("Nie mozna dzielic przez 0!"); return; } System.out.println("Wynik dzielenia: " + (x / y)); } }
Powyższa metoda wypiszWynikDzielenia ma za zadanie wypisać wynik dzielenia dwóch podanych liczb i nic nie zwraca (void). Na początku metody sprawdzamy, czy dzielnik nie jest równy 0 – jeżeli tak, to wypisujemy na ekran informację, że przez zero dzielić nie można i kończymy działanie metody wypiszWynikDzielenia z pomocą użycia return – w takim przypadku nie dojdzie do dzielenia. Wynik działania tego programu:
Pytanie: co stanie się, jeżeli zdefiniujemy, że metoda ma zwracać wartość, ale nie użyjemy return w tej metodzie?
Spójrzmy na poniższy przykład, w którym metoda kwadratLiczby powinna zwrócić wartość typu int, jednak nie zostało w niej użyte słowo kluczowe return:
public class BrakReturn { public static void main(String[] args) { int liczbaDoKwadratu = kwadratLiczby(5); } public static int kwadratLiczby(int x) { int wynik = x * x; // ups! zapomnielismy zwrocic wynik! } }
Próba kompilacji tego programu kończy się następującym błędem:
Jeżeli zdefiniowaliśmy zwracany typ, to nasza metoda musi zwracać jakąś wartość – inaczej kod się nie skompiluje (chyba, że metoda rzuca wyjątek – zapoznamy się z takim przypadkiem w rozdziale o wyjątkach).
Podobnie, jeżeli metoda ma nic nie zwracać (void), a użyjemy w niej return i podamy jakąś wartość, to próba kompilacji takiego programu także zakończy się błędem:
public class VoidMetodaZwracaWartosc { public static void main(String[] args) { System.out.println("Witaj Swiecie!"); return 5; // blad kompilacji } }
Błąd kompilacji – kompilator informuje nas, że w tej metodzie nie spodziewał się zwracania jakiejkolwiek wartości:
Używanie wartości zwracanych przez metody¶
Wiemy już jak wywoływać metody oraz jak zwracać z nich wartości, ale gdzie w zasadzie możemy użyć wywołania metody i zwracanej przez nią wartości?
Wartość zwracana z metody może być:
- przypisana do zmiennej,
- przesłana jako parametr do innej metody,
- użyta w instrukcji if lub pętli itp.,
- w ogóle nie użyta.
Spójrzmy na kilka przykładów.
Przypisanie wyniku metody do zmiennej¶
Jak już wielokrotnie widzieliśmy w tym rozdziale, wynik działania metody możemy przypisać do zmiennej. Typ zmiennej musi być tego samego typu, co definiowany przez metodę zwracany typ:
public class RezultatMetodyPrzypisanyDoZmiennej { public static void main(String[] args) { int kwadrat = podniesDoKwadratu(16); /* zakomentowana linijka, poniewaz powoduje ona blad kompilacji nie mozna przypisac liczby do zmiennej typu String: error: incompatible types: int cannot be converted to String */ // String tekst = podniesDoKwadratu(16); } public static int podniesDoKwadratu(int liczba) { return liczba * liczba; } }
W powyższym przykładzie przypisujemy wynik wywołania metody podniesDoKwadratu do zmiennej typu int o nazwie kwadrat. Linia z przypisaniem wyniku tej metody do zmiennej typu String jest zakomentowana, ponieważ spowodowałaby ona błąd kompilacji – metoda podniesDoKwadratu definiuje, że będzie zwracać wartość liczbową typu int, a String przechowuje tekst – kompilator zaprotestuje.
Rezultat metody jako argument innej metody¶
Wynik metody możemy nie tylko przypisać do zmiennej, ale podać także jako argument innej metody:
import java.util.Scanner; public class RezultatMetodyJakoArgumentInnejMetody { public static void main(String[] args) { System.out.println("Podaj liczbe, a ja wypisze jej kwadrat:"); System.out.println(podniesDoKwadratu(getInt())); } public static int getInt() { return new Scanner(System.in).nextInt(); } public static int podniesDoKwadratu(int liczba) { return liczba * liczba; } }
W powyższym przykładzie argumentem metody println jest wynik działania metody podniesDoKwadratu. Jednakże, aby ta metoda zwróciła wartość, najpierw musi zostać wywołana metoda getInt, która pobierze od użytkownika liczbę, która zostanie zwrócona i stanie się argumentem metody podniesDoKwadratu. Gdy metoda podniesDoKwadratu wykona się, zwróci liczbę podniesioną do kwadratu, która stanie się z kolei argumentem metody println, służącej do wypisywania tekstu na ekran.
Kolejność wykonania metod będzie więc następująca:
- Najpierw wykona się metoda getInt.
- Następnie, metoda podniesDoKwadratu.
- Na końcu wykona się println, które wypisze wynik podnoszenia liczby do kwadratu na ekran.
Metoda użyta w instrukcji warunkowej¶
Wyniki metod są często używane jako warunki instrukcji warunkowych:
import java.util.Scanner; public class RezultatMetodyWInstrukcjiWarunkowej { public static void main(String[] args) { System.out.println("Podaj liczbe:"); int podanaLiczba = getInt(); // (1) // wywolujemy metode, a nastepnie sprawdzamy wynik // przy pomocy instrukcji warunkowej if if (czyParzysta(podanaLiczba)) { // (2) System.out.println("Ta liczba jest parzysta."); } else { System.out.println("Ta liczba nie jest parzysta."); } System.out.println("Podaj kolejna liczbe:"); // (3) if (getInt() >= 0) { // (4) System.out.println("Podales nieujemna liczbe."); } else { System.out.println("Podales liczbe ujemna."); } } public static int getInt() { return new Scanner(System.in).nextInt(); } public static boolean czyParzysta(int liczba) { // (5) return liczba % 2 == 0; // (6) } }
W powyższym przykładzie dzieje się kilka rzeczy:
- Pobieramy (1) od użytkownika liczbę.
- Jak wiemy, instrukcja warunkowa (2) oczekuje wartość true bądź false – tak się składa, że nasza metoda czyParzysta (5) zwraca wartość typu boolean, a typ ten może mieć jedną z dwóch wartości – właśnie true bądź false. Metoda czyParzysta używaoperatora % (reszta z dzielenia) do sprawdzenia, czy reszta z dzielenia przesłanej w argumencie liczby wynosi 0 i zwraca wynik tego porównania (6).
- Wypisujemy komunikat (3), aby użytkownik podał kolejną liczbę.
- Tym razem (4), nie przypisujemy wyniku metody getInt do żadnej zmiennej, lecz porównujemy wynik działania tej metody (czyli liczbę, którą pobraliśmy od użytkownika) od razu do liczby 0 i wypisujemy komunikat, czy jest ona nieujemna.
Warto tutaj jeszcze dodać, że wynik poniższego porównania:
liczba % 2 == 0 // (6)
wynosi true bądź false i tą wartość możemy zwrócić z metody czyParzysta. Powyższy kod moglibyśmy zapisać w mniej zwięzłej formie jako:
public static boolean czyParzysta(int liczba) { boolean czyLiczbaJestParzysta; if (liczba % 2 == 0) { czyLiczbaJestParzysta = true; } else { czyLiczbaJestParzysta = false; } return czyLiczbaJestParzysta; }
czy też:
public static boolean czyParzysta(int liczba) { if (liczba % 2 == 0) { return true; } else { return false; } }
Nieużywanie wyniku metody¶
Wartość zwracana z metody nie musi zostać nigdzie użyta – nie ma takiego wymogu. Czasem po prostu chcemy zignorować zwracaną wartość, bo nie jest nam ona do niczego potrzebna:
import java.util.Scanner; public class RezultatMetodyNieUzyty { public static void main(String[] args) { int liczba = getInt(); // pobieramy od uzytkownika druga liczbe, // ale nigdzie jej nie uzywamy getInt(); } public static int getInt() { return new Scanner(System.in).nextInt(); } }
Nieosiągalne ścieżki wykonania i ścieżki bez return¶
Jak wiemy z jednego z poprzednich rozdziałów, gdy metoda definiuje, że będzie zwracać wartość, a nie użyjemy słowa kluczowego return, by zwrócić z niej jakąś wartość, nasz program się nie skompiluje:
public class BrakReturn { public static void main(String[] args) { int liczbaDoKwadratu = kwadratLiczby(5); } public static int kwadratLiczby(int x) { int wynik = x * x; // ups! zapomnielismy zwrocic wynik! } }
Spójrzmy na bardziej skomplikowany przykład, w którym uzależniamy zwracaną wartość od pewnego warunku – jaki będzie wynik działania tego programu?
public class ReturnWIfie { public static void main(String[] args) { double wynikDzielenia = podzielLiczby(100, 25); } public static double podzielLiczby(int x, int y) { if (y != 0) { return x / y; } } }
Program w ogóle się nie skompiluje! Powodem jest to, że istnieje taka ścieżka wykonania metody podzielLiczby, w której metoda ta nie zwróci żadnej wartości – jeżeli wartość argumentu y będzie wynosić 0, to metoda nie zwróci wartości.
Kompilator, analizując nasz kod źródłowy, jest w stanie wychwycić takie przypadki i zasygnalizować błąd jeszcze na etapie kompilacji:
Kompilator wskazuje błąd w linijce, która zawiera klamrę zamykającą metodę podzielLiczby. dając znam do zrozumienia, że w tym miejscu spodziewał się zwrotu wartości z metody za pomocą return.
Wartość z metody musi zostać zwrócona w każdej możliwej jej ścieżce wykonania – inaczej program się nie skompiluje (o ile dana ścieżka wykonania nie kończy się rzuceniem wyjątku – w takim przypadku zwrócenie wartości nie jest wymagane, ale o takim przypadku opowiemy sobie więcej, gdy będziemy uczyli się o wyjątkach.)
Nieosiągalny kod¶
Kompilator jest także w stanie wykryć instrukcje w metodzie, które nie mają szansy zostać wykonane, tak jak w poniższym przykładzie:
public class InstrukcjaPoReturn { public static void main(String[] args) { int kwadrat = liczbaDoKwadratu(10); } public static int liczbaDoKwadratu(int x) { return x * x; // linia po return nie ma szansy sie wykonac System.out.println("Instrukcja po return!"); } }
Kompilacja tego programu zakończy się poniższym błędem, ponieważ kompilator wykrył, że instrukcja System.out.println("Instrukcja po return!"); nie ma szansy się wykonać – instrukcja return, znajdująca się nad nią, spowoduje zakończenie metody i zwrócenie wartości.
Void, czyli niezwracanie wartości¶
Na koniec rozdziału o zwracanych przez metody wartościach spójrzmy na przypadek szczególny, w którym... metoda nic nie zwraca. Jak już wiemy, w takim przypadku, zamiast podawać zwracany typ przed nazwą metody, używamy słowa kluczowego void.
Pytanie: co się stanie, jeżeli spróbujemy przypisać wynik metody, która nic nie zwraca, do, na przykład, zmiennej?
public class BrakZwracanejWartosciPrzypisanie { public static void main(String[] args) { int x = wypiszKomunikat("Wczoraj padal deszcz."); } public static void wypiszKomunikat(String komunikat) { System.out.println("Uwaga - komunikat!"); System.out.println(komunikat); } }
Powyższy program w ogóle się nie skompiluje. Skoro nasza metoda wypiszKomunikat nic nie zwraca, to nie powinniśmy próbować przypisać zwracanego przez nią wyniku (ponieważ takowego nie będzie) do zmiennej. Kompilator wyświetli komunikat o błędzie, w którym poinformuje nas, że nie może przypisać "niczego" do zmiennej typu int:
Podsumowanie do zwracania wartości¶
- Metody mogą zwracać wartość typu prymitywnego (np. int) oraz złożonego (np. String).
- Jeżeli metoda ma nic nie zwracać, zamiast podawać zwracany typ używamy słowa kluczowego void. Poniższa metoda main nie zwraca żadnej wartości:
public static void main(String[] args) { System.out.println("Witaj Swiecie!"); }
- Zwracany typ musi zawsze poprzedzać nazwę metody – poniższy kod jest niepoprawny:
// blad! void musi byc zaraz przed main void public static main(String[] args) { System.out.println("Witaj Swiecie!"); }
- Aby zwrócić wartość z metody, używamy słowa kluczowego return, po którym następuje wartość, którą chcemy zwrócić.
- Użycie słowa kluczowego return natychmiast przerywa metodę i zwraca wartość.
- Metoda może zawierać więcej niż jedno użycie return, ponieważ możemy uzależnić zwracaną wartość od pewnego warunku:
public static int ktoraWieksza(int a, int b) { if (a > b) { return a; } else { return b; } }
- Metody, które nic nie zwracają (void) także mogą używać słowa kluczowego return, aby przerwać działanie metody:
public static void wypiszWynikDzielenia(int x, int y) { if (y == 0) { System.out.println("Nie mozna dzielic przez 0!"); return; } System.out.println("Wynik dzielenia: " + (x / y)); }
- Jeżeli metoda definiuje, że będzie zwracała wartość, a nic z niej nie zwrócimy, to nasz program się nie skompiluje:
public static int kwadratLiczby(int x) { int wynik = x * x; // ups! zapomnielismy zwrocic wynik! blad kompilacji }
- Wartość zwracana przez metodę może być:
- Przypisana do zmiennej:
int kwadrat = podniesDoKwadratu(16);
- Przesłana jako parametr do innej metody:
System.out.println(podniesDoKwadratu(getInt()));
- Użyta w instrukcji if lub pętli itp.:
if (czyParzysta(podanaLiczba)) { System.out.println("Ta liczba jest parzysta."); } else { System.out.println("Ta liczba nie jest parzysta."); }
- W ogóle nie użyta:
public class RezultatMetodyNieUzyty { public static void main(String[] args) { int liczba = getInt(); // pobieramy od uzytkownika druga liczbe, // ale nigdzie jej nie uzywamy getInt(); } public static int getInt() { return new Scanner(System.in).nextInt(); } }
- Przypisana do zmiennej:
- Jeżeli metoda definiuje, że nie będzie nic zwracać (void), to nie możemy jej przypisać do zmiennej, ani użyć w instrukcji if, pętli itp. – poniższy kod się nie skompiluje:
public class BrakZwracanejWartosciPrzypisanie { public static void main(String[] args) { // blad – probujemy przypisac wynik metody do zmiennej, // a metoda ta nie zwraca zadnej wartosci (void) int x = wypiszKomunikat("Wczoraj padal deszcz."); } public static void wypiszKomunikat(String komunikat) { System.out.println("Uwaga - komunikat!"); System.out.println(komunikat); } }
- Jeżeli metoda ma wiele ścieżek wykonania (np. używamy w niej instrukcji if), to w każdej możliwej ścieżce wykonania tej metody musi znajdować się instrukcja return (wyjątkiem od tej reguły jest użycie wyjątków, o których będziemy się wkrótce uczyć). Poniższa metoda spowodowałaby, że program by się nie skompilował, ponieważ metoda nie zwróci żadnej wartości, gdy y == 0:
public static double podzielLiczby(int x, int y) { if (y != 0) { return x / y; } // brakuje return w przypadku, gdy y == 0! blad kompilacji }
- Jeżeli po instrukcji return będą znajdować się jakieś instrukcje, to kod takiej metody się nie skompiluje, gdyż kompilator jest w stanie wychwycić takie przypadki i zaprotestować:
public static int liczbaDoKwadratu(int x) { return x * x; // linia po return nie ma szansy sie wykonac – blad kompilacji System.out.println("Instrukcja po return!"); }
Pytania do zwracania wartości¶
- Jak zwrócić z metody wartość?
- Czy metoda, która zwraca liczbę, może także zwrócić tekst (String)?
- Czy metoda może nic nie zwracać, a jeśli tak, to jak to osiągnąć?
- Czy możemy użyć słowa kluczowego return więcej niż raz w metodzie?
- Czy możemy użyć słowa kluczowego return w metodzie, która nic nie zwraca?
- Jeżeli metoda ma zwrócić wartość typu double, ale nie użyjemy w niej return, czy kod się skompiluje?
- Gdzie możemy użyć wartości zwracanej przez metodę?
- Czy wartość zwrócona przez metodę musi zostać zawsze użyta?
- Czy poniższy kod jest poprawny (kod klasy opakowującej metody został pominięty)?
public static void main(String[] args) { String tekst = podniesDoKwadratu(16); } public static int podniesDoKwadratu(int liczba) { return liczba * liczba; }
- Które z poniższych metod są nieprawidłowe i dlaczego?
public static wypiszKomunikat() { System.out.println("Witajcie!"); }
public static void x() {}
public static void getInt() { return new Scanner(System.in).nextInt(); }
public static int doKwadratu(int c) { int wynik = c * c; }
public static int podzielLiczby(int a, int b) { if (b == 0) { return; } return a / b; }
public static String getInt() { return new Scanner(System.in).nextInt(); }
public static int ktoraNajwieksza(int a, int b, int c) { if (a > b) { if (a > c) { return a; } } else { if (c > b) { return c; } else { return b; } } }
public static void wypiszKwadrat(int a) { System.out.println("Kwadrat wynosi: " + a * a); return; System.out.println("Policzone!"); }
public static void wypiszPowitanie { System.out.println("Witajcie!"); }
Zadania do zwracania wartości¶
Metoda podnosząca do sześcianu¶
Napisz metodę, która zwróci liczbę przesłaną jako argument podniesioną do sześcianu.
Metoda wypisująca gwiazdki¶
Napisz metodę, która wypisze podaną liczbę gwiazdek (znak *) na ekran.