CodeGym /Blog Java /Poland /Gettery i settery w Javie
Autor
Vasyl Malik
Senior Java Developer at CodeGym

Gettery i settery w Javie

Opublikowano w grupie Poland
Cześć! Na poprzednich lekcjach nauczyłeś/aś się deklarować swoje własne, w pełni wyposażone klasy z polami i metodami. To imponujący postęp, gratulacje! A teraz ta zła wiadomość. Nie zadeklarowaliśmy tych klas w poprawny sposób! Jak to możliwe? Na pierwszy rzut oka wszystko z poniższą klasą jest w porządku:

public class Cat {

   public String name;
   public int age;
   public int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}
A jednak to nieprawda. Wyobraź sobie, że jesteś w pracy i tworzysz tę klasę Cat, która ma reprezentować koty. A potem idziesz do domu. Do pracy przychodzi po Tobie ktoś inny. Tworzy swoją własną klasę Main, w której zaczyna używać Twojej klasy Cat.

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";
       cat.age = -1000;
       cat.weight = 0;
   }
}
Nie ma znaczenia, dlaczego i w jaki sposób to robi (może się nie wyspał). Liczy się coś innego: nasza bieżąca klasa Cat pozwala, aby do jej pól przypisano jakieś kompletnie wariackie wartości. W efekcie program zawiera obiekty z nieprawidłowym stanem (na przykład, że kot ma -1000 lat). Jaki zatem błąd zrobiliśmy przy deklarowaniu naszej klasy? Odsłoniliśmy jej dane Pola: name, age i weight są publiczne. Można mieć do nich dostęp z każdego miejsca w programie: wystarczy stworzyć obiekt Cat, a dowolny programista będzie miał bezpośredni dostęp do jego danych poprzez operator kropka (.).

Cat cat = new Cat();
cat.name = "";
Tutaj mamy bezpośredni dostęp do pola name i ustawiamy jego wartość. Powinniśmy w jakiś sposób uchronić nasze dane przed niepożądanym zmienianiem ich przez obcych. Co zatem możemy zrobić? Po pierwsze, wszystkie zmienne instancji (pola) powinny być oznaczone modyfikatorem private. Private to najbrdziej restrykcyjny modyfikator w Javie. Kiedy już to zrobimy, do pól klasy Cat nie będzie można uzyskać dostępu spoza tej klasy.

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";//error! The Cat class's name field is private!
   }
}
Kompilator to zauważa i automatycznie generuje informację o błędzie. Teraz pola są w pewnym sensie chronione. Ale być może za bardzo zawęziliśmy dostęp: nie można na przykład odczytać wagi kota istniejącego w programie, nawet jeśli jest to potrzebne. Chyba się nie popisaliśmy. Przez taki zabieg nasza klasa jest praktycznie bezużyteczna. Idealnym wyjściem byłoby zezwolenie na pewien ograniczony dostęp:
  • Inni programiści powinni móc tworzyć obiekty Cat.
  • Dobrze byłoby, gdyby mieli także możliwość odczytywania danych z istniejących obiektów (np. odczytu imienia i wieku istniejącego kota)
  • Przypisywanie wartości polom również powinno być możliwe. Dozwolone jednak powinny być tylko prawidłowe wartości. To, co musimy zrobić, to ochronić nasze obiekty przed nieprawidłowymi wartościami (np. age = -1000 itp.)
Wow, całkiem sporo tych wymagań! W rzeczywistości wszystko to można łatwo osiągnąć dzięki zastosowaniu specjalnych metod o nazwach: gettery i settery w Javie. Nazwy swoje zawdzięczają słowom „get” (pol. pobrać, otrzymać - metoda do pobierania wartości z pola) oraz „set” (pol. ustawiać - metoda do ustawiania wartości pola). Spójrzmy, jak by to wyglądało w naszej klasie Cat:

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public int getWeight() {
       return weight;
   }

   public void setWeight(int weight) {
       this.weight = weight;
   }
}
Jak sam/a widzisz, bułka z masłem :) Ich nazwy zazwyczaj składają się z get/set plus nazwa odpowiedniego pola. Przykładowo, metoda getWeight() zwraca wartość pola weight obiektu, na którym jest wywoływana. W programie wygląda to tak:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       String smudgeName = smudge.getName();
       int smudgeAge = smudge.getAge();
       int smudgeWeight = smudge.getWeight();

       System.out.println("Cat's name: " + smudgeName);
       System.out.println("Cat's age: " + smudgeAge);
       System.out.println("Cat's weight: " + smudgeWeight);
   }
}
Wyjście konsoli:
Cat's name: Smudge
Cat's age: 5
Cat's weight: 4
Teraz inna klasa (Main) może mieć dostęp do pól klasy Cat, ale tylko przy stosowaniu getterów. Zauważ, że gettery mają modyfikator dostępu public, tzn., że można mieć do nich dostęp z każdego miejsca w programie. A co z przypisywaniem wartości? Przecież po to właśnie są metody setter.

public void setName(String name) {
   this.name = name;
}
Jak widzisz, to też było proste. Wywołaliśmy metodę setName() na obiekcie Cat, przekazując string jako argument, a string został przypisany do pola obiektu name.

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);

       System.out.println("Cat's original name: " + smudge.getName());
       smudge.setName("Mr. Smudge");
       System.out.println("Cat's new name: " + smudge.getName());
   }
}
Tutaj używamy zarówno getterów, jak i setterów. Najpierw stosujemy getter, aby pobrać i wyświetlić pierwotne imię kota. A następnie używamy settera, aby przypisać mu nowe imię ("Pan Kafel"). Potem ponownie używamy gettera, aby pobrać imię (sprawdzamy, czy naprawdę się zmieniło). Wyjście konsoli:
Cat's original name: Smudge
Cat's new name: Mr. Smudge
Gettery i settery w Javie - 1Czym to się różni od tego, co robiliśmy wcześniej? Przecież wciąż możemy przypisać do pól nieprawidłowe wartości, nawet z setterami:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Wyjście konsoli:
Smudge's age: -1000 years
Różnica jest taka, że setter jest metodą kompleksową. I w przeciwieństwie do pola, metoda pozwala napisać logikę weryfikującą, dzięki której unikniesz wartości niedozwolonych. Na przykład, w prosty sposób możesz uniemożliwić przypisanie ujemnej wartości do pola age:

public void setAge(int age) {
   if (age >= 0) {
       this.age = age;
   } else {
       System.out.println("Error! Age can't be negative!");
   }
}
I teraz nasz kod działa poprawnie!

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Wyjście konsoli:
Error! Age can't be negative!
Smudge's age: 5 years
Wewnątrz settera dodaliśmy kontrolę, która chroni nas przed próbą wprowadzenia nieprawidłowych danych. Wiek Kafla nie został zmieniony. Gettery i settery w Javie to coś, co powinno się tworzyć zawsze. Nawet jeśli nie ma ograniczeń co do tego, jakie wartości pola mogą przyjąć, te metody pomocnicze na pewno nie zaszkodzą. Wyobraź sobie taką sytuację: piszesz program razem z kolegami. Tworzysz klasę Cat z polami public. Wszyscy używają jej, jak tylko chcą. I pewnego dnia nagle sobie coś uświadamiasz: „Niech to szlag, prędzej czy później ktoś przypisze ujemną wartość do wagi zupełnie przypadkiem! Musimy utworzyć settery i ustawić wszystkie pola na private!” Robisz to i od razu psujesz cały kod utworzony przez Twoich kolegów. A przecież napisali już oni kawał dobrego kodu, który ma bezpośredni dostęp do pól klasy Cat.

cat.name = "Behemoth";
Teraz pola są prywatne, a kompilator wypluwa morze błędów!

cat.name = "Behemoth";//error! The Cat class's name field is private!
W takim przypadku byłoby lepiej ukryć pola i utworzyć gettery i settery od początku. Wszyscy Twoi koledzy by ich używali. A jeśli Ty poniewczasie zorientowałeś/aś się, że trzeba było w jakiś sposób ograniczyć wartości, mogłeś/aś zwyczajnie napisać sprawdzenie wewnątrz settera. Dzięki temu kod nie uległby zniszczeniu. Oczywiście, jeśli chcesz, aby pole było „tylko do odczytu”, możesz dla niego utworzyć sam getter. Dostępne z zewnątrz powinny być tylko metody (tzn. spoza Twojej klasy). Dane powinny pozostać ukryte. Możemy to porównać do telefonu. Pomyśl sobie, że zamiast zwyczajnie obudowanego telefonu dostajesz do ręki telefon z otwartą obudową, wystającymi kabelkami, drucikami itp. Telefon działa: jeśli bardzo się postarasz i połączysz odpowiednie kable, może nawet uda Ci się do kogoś zadzwonić. Ale raczej skończy się tak, że go zepsujesz.
Gettery i settery w Javie - 2
A teraz wyobraź sobie, że producent daje Ci interfejs, a Ty jako użytkownik możesz po prostu nacisnąć odpowiednie cyfry, zieloną słuchawkę i bez problemu zadzwonić. Nie obchodzi Cię, co dzieje się z kabelkami wewnątrz telefonu i jak wykonują swoją pracę. W tym przypadku producent ograniczył dostęp do „wnętrzności” telefonu (danych) i pokazał tylko sam interfejs (metody). Efekt jest taki, że użytkownik dostaje to, czego chce (możliwość wykonania telefonu) i z pewnością nie zepsuje niczego w środku.
Ten artykuł przeczytasz także po angielsku.
Read the English version of this article to learn more about getters and setters in Java.
Komentarze (1)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
selkain Poziom 6, Cambridge, Polska
27 kwietnia 2022
Przydało się do rozwiązania zadania z getterami i setterami.🤩