CodeGym/Blog Java/Random-PL/Zagnieżdżone klasy wewnętrzne
Autor
John Selawsky
Senior Java Developer and Tutor at LearningTree

Zagnieżdżone klasy wewnętrzne

Opublikowano w grupie Random-PL
Cześć! Dzisiaj zajmiemy się ważnym tematem - jak działają klasy zagnieżdżone w Javie. Java pozwala tworzyć klasy wewnątrz innej klasy:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Te wewnętrzne klasy są nazywane zagnieżdżonymi. Dzielą się na 2 rodzaje:
  1. Niestatyczne klasy zagnieżdżone. Są one również nazywane klasami wewnętrznymi.
  2. Statyczne klasy zagnieżdżone.
Z kolei klasy wewnętrzne mają dwie odrębne podkategorie. Oprócz tego, że klasa wewnętrzna jest po prostu klasą wewnętrzną, może to być również:
  • lokalna klasa
  • anonimowa klasa
Zdezorientowany? :) W porządku. Oto schemat dla jasności. Wróć do niego podczas lekcji, jeśli nagle poczujesz się zdezorientowany! Zagnieżdżone klasy wewnętrzne — 2W dzisiejszej lekcji omówimy klasy wewnętrzne (znane również jako niestatyczne klasy zagnieżdżone). Są one specjalnie wyróżnione na ogólnym diagramie, żeby się nie zgubić :) Zacznijmy od oczywistego pytania: dlaczego nazywamy je klasami „wewnętrznymi”? Odpowiedź jest dość prosta: ponieważ są tworzone wewnątrz innych klas. Oto przykład:
public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Tutaj mamy Bicycleklasę. Ma 2 pola i 1 metodę: start(). Zagnieżdżone klasy wewnętrzne — 3Różni się od zwykłej klasy tym, że zawiera dwie klasy: Handlebari Seat. Ich kod jest napisany wewnątrz Bicycleklasy. Są to pełnoprawne klasy: jak widać, każda z nich ma swoje własne metody. W tym momencie możesz mieć pytanie: dlaczego, u licha, mielibyśmy umieszczać jedną klasę w drugiej? Po co robić z nich klasy wewnętrzne? Załóżmy, że w naszym programie potrzebujemy osobnych klas dla pojęć kierownicy i siodełka. Oczywiście nie musimy ich zagnieżdżać! Możemy zrobić zwykłe zajęcia. Na przykład tak:
public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
Bardzo dobre pytanie! Oczywiście nie ogranicza nas technologia. Zrobienie tego jest z pewnością opcją. Tutaj ważniejsze jest bardziej poprawne zaprojektowanie zajęć z punktu widzenia konkretnego programu i jego przeznaczenia. Klasy wewnętrzne służą do oddzielenia jednostki, która jest nierozerwalnie połączona z inną jednostką. Kierownica, siodełko i pedały są elementami roweru. Oddzielone od roweru nie mają większego sensu. Gdybyśmy rozdzielili wszystkie te koncepcje na klasy publiczne, w naszym programie mielibyśmy taki kod:
public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... Znaczenie tego kodu jest nawet trudne do wyjaśnienia. Mamy jakąś niejasną kierownicę (Dlaczego to konieczne? Nie mam pojęcia, szczerze mówiąc). A ten uchwyt skręca w prawo... całkiem sam, bez roweru... z jakiegoś powodu. Oddzielając koncepcję kierownicy od koncepcji roweru, straciliśmy trochę logiki w naszym programie. Używając klasy wewnętrznej, kod wygląda zupełnie inaczej:
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
Wyjście konsoli:
Seat up!
Let's go!
Steer left!
Steer right!
Teraz to, co widzimy, nagle nabiera sensu! :) Stworzyliśmy obiekt rowerowy. Stworzyliśmy dwa „podobiekty” rowerowe — kierownicę i siodełko. Podnieśliśmy siedzenie dla wygody i ruszyliśmy: pedałowanie i sterowanie w razie potrzeby! :) Metody, których potrzebujemy, są wywoływane na odpowiednich obiektach. To wszystko jest proste i wygodne. W tym przykładzie oddzielenie kierownicy i siodełka poprawia enkapsulację (ukrywamy dane o częściach roweru w odpowiedniej klasie) i pozwala nam stworzyć bardziej szczegółową abstrakcję. Spójrzmy teraz na inną sytuację. Załóżmy, że chcemy stworzyć program symulujący sklep rowerowy i części zamienne do rowerów. Zagnieżdżone klasy wewnętrzne — 4W tej sytuacji nasze poprzednie rozwiązanie nie będzie działać. W sklepie rowerowym każda pojedyncza część roweru ma sens, nawet jeśli jest oddzielona od roweru. Na przykład będziemy potrzebować metod takich jak „sprzedaj pedały klientowi”, „kup nowe siodełko” itp. Błędem byłoby stosowanie tutaj klas wewnętrznych — każda pojedyncza część roweru w naszym nowym programie ma znaczenie, które opiera się na własny: można go oddzielić od koncepcji roweru. To jest dokładnie to, na co musisz zwrócić uwagę, jeśli zastanawiasz się, czy powinieneś używać klas wewnętrznych, czy organizować wszystkie jednostki jako osobne klasy. Programowanie zorientowane obiektowo jest dobre, ponieważ ułatwia modelowanie rzeczywistych obiektów. Może to być twoją przewodnią zasadą przy podejmowaniu decyzji, czy użyć klas wewnętrznych. W prawdziwym sklepie części zamienne są oddzielone od rowerów — to jest w porządku. Oznacza to, że jest to również w porządku podczas projektowania programu. Dobra, „filozofię” mamy już za sobą :) A teraz zapoznajmy się z ważnymi „technicznymi” cechami klas wewnętrznych. Oto, co zdecydowanie musisz zapamiętać i zrozumieć:
  1. Obiekt klasy wewnętrznej nie może istnieć bez obiektu klasy zewnętrznej.

    To ma sens: dlatego stworzyliśmy w naszym programie zajęcia Seati Handlebar— aby nie skończyć z osieroconymi kierownicami i siedzeniami.

    Ten kod się nie kompiluje:

    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }

    Wynika z tego jeszcze jedna ważna cecha:

  2. Obiekt klasy wewnętrznej ma dostęp do zmiennych klasy zewnętrznej.

    Na przykład dodajmy int seatPostDiameterdo naszej klasy zmienną (reprezentującą średnicę sztycy) Bicycle.

    Następnie w Seatklasie wewnętrznej możemy stworzyć displaySeatProperties()metodę wyświetlającą właściwości siedzenia:

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    A teraz możemy wyświetlić te informacje w naszym programie:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }

    Wyjście konsoli:

    Seat properties: seatpost diameter = 40

    Notatka:nowa zmienna jest deklarowana z najbardziej rygorystycznym modyfikatorem dostępu ( private). A mimo to klasa wewnętrzna ma dostęp!

  3. Obiekt klasy wewnętrznej nie może zostać utworzony w metodzie statycznej klasy zewnętrznej.

    Wyjaśnia to specyfika organizacji klas wewnętrznych. Klasa wewnętrzna może mieć konstruktory z parametrami lub tylko konstruktor domyślny. Ale niezależnie od tego, kiedy tworzymy obiekt klasy wewnętrznej, referencja do obiektu klasy zewnętrznej jest niewidocznie przekazywana do tworzonego obiektu klasy wewnętrznej. W końcu obecność takiego odniesienia do obiektu jest bezwzględnym wymogiem. W przeciwnym razie nie będziemy mogli tworzyć obiektów klasy wewnętrznej.

    Ale jeśli metoda klasy zewnętrznej jest statyczna, możemy nie mieć obiektu klasy zewnętrznej! Byłoby to naruszeniem logiki działania klasy wewnętrznej. W tej sytuacji kompilator wygeneruje błąd:

    public static Seat createSeat() {
    
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. Klasa wewnętrzna nie może zawierać zmiennych statycznych i metod.

    Logika jest taka sama: statyczne metody i zmienne mogą istnieć i być wywoływane lub przywoływane nawet w przypadku braku obiektu.

    Ale bez obiektu klasy zewnętrznej nie będziemy mieli dostępu do klasy wewnętrznej.

    Wyraźna sprzeczność! Dlatego statyczne zmienne i metody nie są dozwolone w klasach wewnętrznych.

    Kompilator wygeneruje błąd, jeśli spróbujesz je utworzyć:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. Podczas tworzenia obiektu klasy wewnętrznej ważny jest jego modyfikator dostępu.

    Klasę wewnętrzną można oznaczyć za pomocą standardowych modyfikatorów dostępu: public, private, protected, i package private.

    Dlaczego to ma znaczenie?

    Wpływa to na to, gdzie możemy tworzyć instancje klasy wewnętrznej w naszym programie.

    Jeśli nasza Seatklasa jest zadeklarowana jako public, możemy tworzyć Seatobiekty w dowolnej innej klasie. Jedynym wymaganiem jest istnienie obiektu klasy zewnętrznej.

    Nawiasem mówiąc, zrobiliśmy to już tutaj:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }

    Łatwo uzyskaliśmy dostęp do Handlebarklasy wewnętrznej z Mainklasy.

    Jeśli zadeklarujemy klasę wewnętrzną jako private, będziemy mogli tworzyć obiekty tylko wewnątrz klasy zewnętrznej.

    Nie możemy już tworzyć Seatobiektu „na zewnątrz”:

    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    Pewnie już rozumiesz logikę :)

  6. Modyfikatory dostępu dla klas wewnętrznych działają tak samo jak dla zwykłych zmiennych.

    Modyfikator protectedzapewnia dostęp do zmiennej instancji w podklasach i klasach, które znajdują się w tym samym pakiecie.

    protecteddziała również dla klas wewnętrznych. Możemy tworzyć protectedobiekty klasy wewnętrznej:

    • w klasie zewnętrznej;
    • w swoich podklasach;
    • w klasach, które są w tym samym pakiecie.

    Jeśli klasa wewnętrzna nie ma modyfikatora dostępu ( package private), można utworzyć obiekty klasy wewnętrznej:

    • w klasie zewnętrznej;
    • w klasach, które są w tym samym pakiecie.

    Od dawna znasz modyfikatory, więc tutaj nie ma problemów.

To wszystko na teraz :) Ale nie obijaj się! Zajęcia wewnętrzne to dość obszerny temat, który będziemy dalej omawiać w następnej lekcji. Teraz możesz odświeżyć swoją pamięć o lekcji naszego kursu dotyczącej zajęć wewnętrznych . A następnym razem porozmawiajmy o statycznie zagnieżdżonych klasach.
Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy