Site icon Java blog

Java #9: dziedziczenie oraz modyfikator protected

java inheritance

Jedną z zasad właściwego programowania jest niepowtarzanie raz zaimplementowanego kodu. Czasami jest to trudne, ponieważ, aby użyć tych samych pól i metod w innych klasach musielibyśmy:

  1. używać programowania strukturalnego (czyli słowa kluczowego static), co łamie zasady programowania obiektowego, więc odrzucam tą opcję,
  2. użycie kompozycji (np. pola, które będzie typem referencyjnym do obiektu, z którego chcemy skorzystać)

Wpływ hermetyzacji na dziedziczenie

Jednak istnieje jeszcze jeden sposób, w jaki można przekazać Twojej klasie informacje z innej klasy. Nazywa się ono dziedziczeniem. Najprościej można je zrozumieć jako skorzystanie z pewnych funkcjonalności klasy bazowej w klasach potomnych. Warto podkreślić, że będą one tylko dostępne dla innej klasy poprzez dziedziczenie (dla innych klas te zmienne i metody będą zachowywały się jak przy użyciu modyfikatora dostępu private).

Spójrz na poniższy przykład:

public class Parent {
  public String publicName;
  Long packageNumber;
  protected Double protectedNumber;
  private Integer privateNumber;
  
  public Parent(String publicName, Long packageNumber, Double protectedNumber, Integer privateNumber) {
    this.publicName = publicName;
    this.packageNumber = packageNumber;
    this.protectedNumber = protectedNumber;
    this.privateNumber = privateNumber;
  }

  public String getPublicName() {
    return publicName;
  }

  public Long getPackageNumber() {
    return packageNumber;
  }

  public Double getProtectedNumber() {
    return protectedNumber;
  }

  public Integer getPrivateNumber() {
    return privateNumber;
  }
}
public class Child extends Parent{

  public Child(String publicName, Long packageNumber, Double protectedNumber, Integer privateNumber) {
    super(publicName, packageNumber, protectedNumber, privateNumber);
  }
  
  public void checkValues() {
    String name = super.publicName;
    Long packageNumber = super.packageNumber;
    Double protectedNumber = super.protectedNumber;
    
    System.out.println("Test 1) " + name + " " + packageNumber + " " + protectedNumber);
    
    String nameByGet = super.getPublicName();
    Long packageNumberByGet = super.getPackageNumber();
    Double protectedNumberByGet = super.getProtectedNumber();
    Integer privateNumberByGet = super.getPrivateNumber();
    
    System.out.println("Test 2) " + nameByGet + " " + packageNumberByGet + " " + protectedNumberByGet + " " + privateNumberByGet);
  }
  
}
public class MainApp {
  public static void main(String args []) {
    Child child = new Child("test child", 10l, 20.0, 30);
    child.checkValues();	
  }
}

Modyfikator protected

Magia dziedziczenia zadziałała. Wystarczyło dodać do nazwy klasy słowo kluczowe extends i wypisać nazwą klasy rodzica. Jak widzisz, pomimo, że klasa Child nie posiada żadnych pól definicji swojej klasy, to mogła skorzystać z tych, które zadeklarowałem w klasie Parent. Były to pola publiczne, pakietowa oraz chronione (protected). Dodatkowo, chociaż pole prywatne privateNumber nie zostało odziedziczone (z reguły pola będziesz pisał/a z prywatnym modyfikatorem dostępu), to mogłem się do niego odwołać przez publiczny getter.

Dziedziczenie – reguły

Sama zasada dziedziczenia wydaje się prosta, jednak ma kilka dodatkowych reguł, o których możesz nie wiedzieć.

Teraz zagadka. Co się stało, gdybym nie wywołał konstruktora bazowego (rodzica) w konstruktorze dziecka?

  public Child(String publicName, Long packageNumber, Double protectedNumber, Integer privateNumber) {
//		super(publicName, packageNumber, protectedNumber, privateNumber);
  }

Kompilator teraz zaświeci na czerwono pierwszą linię i wyświetli informację: „Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor„. Jeśli nie wywołasz konstruktora bazowego, nie uda się stworzyć konstruktora w klasie potomnej. Dzieje się tak dlatego, bo dziecko 'tworzy’ też obiekt rodzica, aby pomóc skorzystać z jego właściwości.

Jest jednak pewien wyjątek od tej reguły. Jeśli rodzic nie będzie posiadał konstruktora, to nie będziemy musieli go wywoływać z klasy potomnej (logiczne). W praktyce jednak, gdy klasa nie ma konstruktora, to Java sama tworzy bezparametrowy konstruktor domyślny (np. Parent() {} ). Czy w takim razie, aby na pewno, w klasie dziecka nie jest użyte słowo super() w pierwszej linii jego konstruktora? Otóż faktycznie, choć go nie widzimy, jest użyty przez kompilator, bowiem przy tworzeniu dziecka zawsze tworzony jest obiekt rodzica.

Link do lekcji: https://github.com/developeronthego/java-basics/tree/main/src/main/java/basics/lesson9

Więcej informacji odnośne modyfikatorów dostępów, znajdziesz w lekcji o enkapsulacji: Java #6: Hermetyzacja (enkapsulacja)

Exit mobile version