Programowanie obiektowe w Javie – konstruktory, metody, pola

Poprzednio omówiliśmy paradygmat programowania obiektowego. Wprowadziliśmy paradygmat programowania obiektowego i pojęcia, takie jak abstrakcja, polimorfizm, hermetyzacja oraz dziedziczenie.

W drugiej części szkolenia poświęconego programowaniu obiektowemu w Javie utworzymy pierwszą własną klasę. Skorzystamy z założeń koncepcyjnych, które opisaliśmy poprzednio. Następnie wyjaśnimy czym są poszczególne linie kodu oraz co oznaczają.

Własna klasa

Zaczynając od przedstawienia założeń klasa samochód będzie zawierała dane, takie jak marka, model, pojemność silnika oraz rodzaj paliwa. Dodatkowo zadbamy o hermetyzację danych i ustawimy wszystkie pola jako prywatne dla klasy. W związku z tym ,aby uzyskać dostęp do danych będziemy potrzebowali użyć specjalne metody dostępu (tzw. gettery).

Zakładamy, że pojemność silnika będzie podawana w cm3. Do zapisania nazwy marki i modelu użyjemy typu String. Ostatni parametr to rodzaj paliwa, który ma z góry określone wartości, zatem użyjemy do tego typu wyliczeniowego enum. Poniższy listing pokazuje naszą implementację klasy Samochod.

Powyższy kod zawiera definicję typu wyliczeniowego enum w celu jednoznacznego nazwania wykorzystywanego przez auto paliwa. Dzięki temu nie uzyskamy dwóch różnych nazw opisujących ten sam rodzaj np. „ropa”, „Diesel”, a to jest istotne przy wyszukiwaniu i filtrowaniu rekordów np w bazie danych. Jednakże w tej części nie będziemy opisywać jak dokładnie działa ten mechanizm.

Pola

Wszystkie atrybuty (zmienne i stałe), które znajdują się w klasie to pola. Mogą być typu prostego (np. double, float, int) lub złożonego (klasa z biblioteki standardowej String lub własny typ).

Inicjacja pól

Pola można zainicjować ustalając ich wartość bezpośrednio w przy deklaracji.

Nie jest to dobra metoda, gdyż każdy tworzony obiekt byłby Polonezem. Jednakże dobrym pomysłem może być uzupełnienie jakimiś sensownymi wartościami domyślnymi.

Podobnie możemy użyć pola inicjującego (w nawiasach klamrowych).

Najczęściej wykorzystywana jest specjalna metoda zwana konstruktorem.

Konstruktor

Element, który jest używany zawsze podczas tworzenia nowej instancji klasy przy pomocy operatora new. Konstruktor może przyjmować parametry lub być bezparametrowy i nigdy nie zwraca żadnej wartości.

Jeżeli nie zdefiniujemy żadnego, to obiekt zostanie utworzony przy pomocy konstruktora domniemanego (domyślnego). W takim przypadku wartości ustawiane są na 0, false lub null w zależności od tego czy rozważamy typ liczbowy, logiczny czy jest obiektem.

Konstruktor zawsze przyjmuje nazwę klasy (obie piszemy zawsze zaczynając od wielkiej litery). Jego głównym zadaniem jest ustawienie początkowych wartości lub wykonanie jakichś czynności. Pod tym względem konstruktor przypomina zwykłą metodę, którą kompilator uruchamia podczas tworzenia obiektu).

W przypadku, gdy napiszemy już własny konstruktor, automatycznie przestanie być używany konstruktor domyślny.

Konstruktory można przeciążać, co oznacza, że może być ich kilka w obrębie danej klasy jednak muszą różnić się liczbą lub typami argumentów. Jest to niezbędne gdyż inaczej kompilator nie potrafiłby użyć odpowiedniego z nich.

Przykład konstruktora znajduje się w listingu poniżej.

Metody

Procedury, które wykonujemy nazywamy metodami. Najczęstszymi ich nazwali są czynności, które wykonują. Metody mogą odczytywać wartości pól oraz je modyfikować. Zgodnie z zasadą hermetyzacji, to właśnie one powinny wpływać na wartości zmiennych obiektu. Dzielimy je na metody:

  • dostępu do wartości pól (gettery),
  • zmiany/modyfikacji pól (settery).

Dostęp do danych prywatnych

Ogólna postać metody dostępu (ang. getter) do danych wygląda następująco:

Modyfikatory dostępu omówimy szczegółowo w następnej części, dlatego na razie uznajmy, metody zawsze są oznaczone jako public. Następna wartość mówi o tym, jakiego typu dane zwróci dana metoda. Metoda może zwrócić typ prosty (jak liczba), własny (obiekt klasy), a nawet tablicę lub kolekcję. Poniżej pokazujemy przykłady.

Ustawianie danych prywatnych

Ogólna postać metody, która modyfikuje dane (ang. setter) wygląda następująco.

Zazwyczaj metoda, która ustawia jakąś wartość pola nie zwraca żadnej wartości, dlatego jest typu void. W nawiasie znajdują się argumenty, które mogą być dowolnego typu. W ciele funkcji ustawiamy pole obiektu wartością pobraną z argumentu.

Metody statyczne

Wszystkie powyższe przykłady, które pokazaliśmy dotyczą metod, które działają na rzecz konkretnego obiektu. Odczytują dane z pól, ustawiają ich wartości lub wykonują różne procedury.

Metody statyczne należą do klas i wywołuje się je podając nazwę klasy, a następnie po kropce podaje się nazwę metody. Zatem nie jest potrzebny żaden obiekt, aby wykonać kod metody statycznej.

Rezultatem będzie wyświetlenie komunikatu „metoda statyczna”.

Pamiętajmy, że przejrzysty kod jest bardzo ważną cechą każdego programu i przed metodą statyczną warto wymienić nazwę klasy, z której ona pochodzi. Więcej na temat słowa kluczowego static i metod statycznych opisujemy w kolejnych częściach szkolenia.

Przekazywanie argumentów do metod

Argumenty w Javie zawsze są przekazywane przez wartość. Jest to duża różnica w stosunku do innych języków, w których można przekazywać np. wskaźniki lub referencje.

Gdy przekazujemy typ prosty jako argument metody tak naprawdę przekazujemy jego kopię. Oznacza to, że zmiana jej wartości w ciele metody nie wpływa na wartość poza nią.

Zróbmy teraz proste ćwiczenie polegające na zamianie zmiennych wartościami.

Wynik działania powyższego kodu jest następujący.

Jak widać kod nie zadziałał zgodnie z oczekiwaniami – wartości zmieniły się jedynie w metodzie – a dokładniej ich kopie. Rozwiązaniem tego problemu w języku C jest przekazanie wskaźników.

Pewną niejasnością może być przekazanie obiektu, gdyż wtedy przekazywaną wartością jest referencja do obiektu.

Rozważmy poniższy kod.

Tym razem przekazaliśmy obiekt do metody zamien(). Po ustawieniu wartości przez setter faktycznie zostaje zmodyfikowana wartość.

W następnej części przyjrzymy się modyfikatorom dostępu i pakietom.