« poprzedni punkt  następny punkt »

3. Definiowanie operacji na obiektach (metody)

Zestaw operacji na obiektach określany jest przez definicję metod klasy.
Pojęcie metody zbliżone jest do znanego już nam pojęcia funkcji.

Metoda - tak samo jak funkcja -  to wyodrębniony zestaw czynności, zapisywany jednorazowo w postaci fragmentu kodu, który może być wywoływany wielokrotnie z innych miejsc programu.

Metody służą głównie (ale nie tylko i niekoniecznie) do wykonywania operacji na obiektach.

Zatem - w odróżnieniu od funkcji -  metody zwykle wywoływane są na rzecz konkretnych obiektów.

Wywołania "na rzecz" obiektu (jak już widzieliśmy) dokonuje się za pomocą "operatora" kropka. Np. jeśli p - oznacza obiekt klasy Para (czyli jest referencją do obiektu klasy Para), a w klasie tej zdefiniowano metodę show, to wywołanie tej metody na rzecz tego obiektu zapisujemy jako:

    p.show();

"Wywołanie na rzecz obiektu" oznacza to samo, co "posłanie polecenia do obiektu" lub "komunikatu do obiektu" lub  "wykonanie operacji na obiekcie".
W tym przypadku (dla metody show):

wywołanie metody show na rzecz obiektu p 
=
posłanie komunikatu/polecenie show do obiektu p
=
wykonanie operacji uwidocznienia obiektu p

Schematyczna postać definicji metody jest następująca:

[specyfikator_dostępu] typ_wyniku nazwa_metody( lista_parametrów ) {
  // ... instrukcje wykonywane po wywołaniu metody
}

Uwagi:
  • nawiasy kwadratowe oznaczają opcjonalność
  • kod zawarty pomiędzy nawiasami klamrowymi nazywany jet ciałem metody

Specyfikator dostępu określa czy metoda może być wywołana spoza klasy, w której jest zdefiniowana. W szczególności:

  • specyfikator public mówi o tym, że dana metoda może być wywołana z dowolnej innej klasy
  • a private - oznacza, że metoda może być wywołana tylko w tej klasie, w której została zdefiniowana

Nazwę metody zaczynamy od małej litery, stosując dalej notację węgierską, np. count, setPrice, getAuthor


Te metody, które chcemy udostępnić jako ogólniedostępne operacje na obiektach oznaczamy słowem public; metody "robocze", które mają znaczenie tylko dla nas (twórców klasy) i nie powinny być dostępne dla innych użytkowników klasy - oznaczamy słowem private.

Lista parametrów zawiera rozdzielone przecinkami deklaracje parametrów, które metoda otrzymuje przy wywołaniu. Lista może być pusta (brak argumentów).

Metoda może zwracać wynik (wtedy w jej definicji musimy podać konkretny typ wyniku, a zakończenie działania metody powinno następować na skutek instrukcji return zwracającej dane podanego typu). Jeśli metoda nie zwraca żadnego wyniku to jej typ wyniku określamy słowem kluczowym void, a metoda może skończyć działanie na skutek dobiegnięcia do zamykającego nawiasu klamrowego lub wykonania instrukcji return bez argumentów

Instrukcja return ma postać:

        return [ wyrażenie ];

Np. metoda zwracająca sumę dwóch liczb całkowitych może wyglądać tak:

int suma(int x, int y) {
    int z = x + y;
    return z;
}

lub tak

int suma(int x, int y) {
    return x + y;
}

Przy wywołaniu metoda suma uzyskuje dwa przekazane jej argumenty jako parametry x i y. Jej działanie polega na dodaniu obu wartości parametrów i zwróceniu (do miejsca wywołania) wyniku. Obowiązkowo, w definicji metody trzeba było podać typ zwracanego wyniku.

Przykład innej metody:

void say(String s) {
    System.out.println(s);
}

Problem
Wywołanie metody say spowoduje wyprowadzenie na konsolę przekazanego jako argument napisu. Metoda nie zwraca żadnego wyniku, mimo to trzeba było określić typ wyniku słowem kluczowym void (dokładnie "nie dotyczy", znaczy - brak wyniku).


W Javie  argumenty przekazywane są metodom wyłącznie przez wartość.
Oznacza to, że w samej metodzie odwołujemy się nie do faktycznego argumentu, ale do jego kopii. Zatem zmiany przekazanego metodzie argumentu są lokalne, dotyczą wyłącznie kopii i nie dotykają oryginału.

Np. po wywołaniu metody:

void incr(int x) {
    ++x;   
 }

ze zmienną  z = 1 jako arguementem w samej metodzie zmienna (parametr) x uzyska wartość 2, ale po zakończeniu działania metody i powrocie sterowania do punktu wywołania zmienna z będzie miała nadal wartość 1.

To samo dotyczy typów obiektowych. Pamiętamy: zmienne oznaczające obiekty zawierają referencje, a nie same obiekty.

Zatem np. w ew. metodzie  przestawPary:

void przestawPary(Para p1, Para p2) {
    Para temp = p1;
    p1 = p2;
    p2 = temp;
}

nie uzyskamy zamierzonego rezultatu, bowiem metoda otrzymuje tylko wartości referencji, a nie odniesienia do nich i wszelkie operacje na tych referencjach dotyczą kopii oryginałów.

Nie znaczy to jednak, że w metodach nie możemy działać na obiektach. Referencje przecież na nie wskazują: trzeba zatem - poprzez nie - odwoływać się do pól i metod klasy i za ich pomocą (jeśli jest to możliwe) zmieniać obiekty. O czym dalej.

W klasie mogą być definiowane metody o tej samej nazwie, ale różniące się liczbą i/lub typami argumentów.
Nazywa się to przeciążaniem metod.
Po co taka możliwość?
Wyobraźmy sobie, że na obiektach  klasy par liczb całkowitych chcielibyśmy wykonywać operacje:

  • dodawania innych obiektów-par
  • dodawania (do składników pary) kolejno dwóch podanych liczb cłakowitych
  • dodawania (do każdego składnika pary) jednej i tej samej podanej  liczby całkowitej

Gdyby nie było przeciążania metod musielibyśmy dla każdej operacji wymyślać inną nazwę metody. A przecież istota operacji jest taka sama (wystarczy więc nazwa add), a jej użycie powinno być jasne z kontekstu (określanego przez argumenty).

Dzięki przeciążaniu można w klasie Para np. zdefiniować metody:

  void add(Para p)  //  dodaje do pary, na rzecz której wywołano metodę, parę
                            //  podaną  jako argument
  void add(int i)      // do obu składników pary dodaje podaną liczbę
  void add(int i, int k) // pierwszą podaną liczbę dodaje do pierwszego składnika pary
                                // a drugą - do drugiego

i  użyć - gdzie indziej - w naturalny sposób:

  Para p;.
  Para jakasPara;
  ....
  p.add(3);             // wybierana jest ta metoda, która pasuje (najlepiej) do argumentów
  p.add(1,2);
  p.add(jakasPara);

Identyfikatory metod definiowanych w klasie muszą być od siebie różne.
Wyjątkiem od tej reguły są metody przeciążone tj. takie, które mają tę samą nazwę (identyfikator), ale różne typy i/lub liczbę argumentów


« poprzedni punkt  następny punkt »