здрасти Днес ще разгледаме важна тема - How работят вложените класове в Java. Java ви позволява да създавате класове в друг клас:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Тези вътрешни класове се наричат ​​вложени. Те са разделени на 2 вида:
  1. Нестатични вложени класове. Те се наричат ​​още вътрешни класове.
  2. Статични вложени класове.
На свой ред вътрешните класове имат две отделни подкатегории. Освен че вътрешният клас е просто вътрешен клас, той може също да бъде:
  • местен клас
  • анонимен клас
объркани? :) Това е добре. Ето диаграма за яснота. Върнете се към него по време на урока, ако изведнъж се окажете объркан! Вложени вътрешни класове - 2В днешния урок ще обсъдим вътрешните класове (известни също като нестатични вложени класове). Те са специално подчертани в общата диаграма, за да не се изгубите :) Да започнем с очевидния въпрос: защо се наричат ​​"вътрешни" класове? Отговорът е доста прост: защото те са създадени в други класове. Ето един пример:

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!");
       }
   }
}
Тук имаме Bicycleкласа. Има 2 полета и 1 метод: start(). Вложени вътрешни класове - 3Той се различава от обикновения клас по това, че съдържа два класа: Handlebarи Seat. Техният code е написан вътре в Bicycleкласа. Това са пълноценни класове: Howто можете да видите, всеки от тях има свои собствени методи. В този момент може да имате въпрос: защо, за бога, бихме поставor един клас в друг? Защо да ги правим вътрешни класове? Е, да предположим, че имаме нужда от отделни класове за понятията кормило и седалка в нашата програма. Разбира се, не е необходимо да ги правим вложени! Можем да правим обикновени класове. Например така:

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!");
   }
}
Много добър въпрос! Разбира се, ние не сме ограничени от технологията. Това със сигурност е вариант. Тук по-важното е правилният дизайн на часовете от гледна точка на конкретна програма и нейната цел. Вътрешните класове са за отделяне на обект, който е неразривно свързан с друг обект. Кормилото, седалките и педалите са компоненти на велосипеда. Отделно от велосипеда нямат особен смисъл. Ако направим всички тези концепции отделни публични класове, щяхме да имаме codeа като този в нашата програма:

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Хм... Значението на този code дори е трудно за обяснение. Имаме няHowво неясно кормило (Защо е необходимо? Нямам представа, честно казано). И тази дръжка се върти надясно... съвсем сама, без велосипед... незнайно защо. Разделяйки концепцията за кормилото от концепцията за велосипеда, загубихме известна логика в нашата програма. Използвайки вътрешен клас, codeът изглежда много различно:

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();
   }
}
Конзолен изход:

Seat up! 
Let's go! 
Steer left! 
Steer right!
Сега това, което виждаме, изведнъж придобива смисъл! :) Създадохме обект велосипед. Създадохме два велосипедни „подобекта“ — кормило и седалка. Повдигнахме седалката за удобство и тръгнахме: педалите и управлението според нуждите! :) Методите, от които се нуждаем, се извикват на съответните обекти. Всичко е просто и удобно. В този пример отделянето на кормилото и седалката подобрява капсулирането (скриваме данни за частите на велосипеда в съответния клас) и ни позволява да създадем по-подробна абстракция. Сега нека разгледаме една различна ситуация. Да предположим, че искаме да създадем програма, която симулира магазин за велосипеди и резервни части за велосипеди. Вложени вътрешни класове - 4В тази ситуация предишното ни решение няма да работи. В магазин за велосипеди всяка отделна част от велосипед има смисъл дори когато е отделена от велосипед. Например, ще ни трябват методи като „продай педали на клиент“, „купи нова седалка“ и т.н. Би било грешка да използваме вътрешни класове тук — всяка отделна велосипедна част в нашата нова програма има meaning, което стои на собствен: може да бъде отделен от концепцията за велосипед. Това е точно това, на което трябва да обърнете внимание, ако се чудите дали да използвате вътрешни класове or да организирате всички обекти като отделни класове. Обектно-ориентираното програмиране е добро с това, че улеснява моделирането на обекти от реалния свят. Това може да бъде вашият ръководен принцип, когато решавате дали да използвате вътрешни класове. В истински магазин, резервните части са отделни от велосипедите - това е добре. Това означава, че е добре и при проектирането на програма. Добре, разбрахме "философията" :) Сега нека се запознаем с важни "технически" характеристики на вътрешните класове. Ето Howво определено трябва да запомните и разберете:
  1. Обект от вътрешен клас не може да съществува без обект от външен клас.

    Това има смисъл: затова направихме Seatи Handlebarвътрешни класове в нашата програма — за да не се окажем с осиротели кормила и седалки.

    Този code не се компorра:

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

    Друга важна характеристика следва от това:

  2. Обект от вътрешен клас има достъп до променливите на външния клас.

    Например, нека добавим int seatPostDiameterпроменлива (представляваща диаметъра на колчето за седалка) към нашия Bicycleклас.

    След това във Seatвътрешния клас можем да създадем displaySeatProperties()метод, който показва свойствата на седалката:

    
    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);
           }
       }
    }
    

    И сега можем да покажем тази информация в нашата програма:

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

    Конзолен изход:

    
    Seat properties: seatpost diameter = 40
    

    Забележка:новата променлива се декларира с най-стриктния модификатор за достъп ( private). И все пак вътрешният клас има достъп!

  3. Обект от вътрешен клас не може да бъде създаден в статичен метод на външен клас.

    Това се обяснява със специфичните особености на организацията на вътрешните класове. Вътрешен клас може да има конструктори с параметри or само конструктор по подразбиране. Но независимо от това, когато създаваме обект от вътрешен клас, препратка към обекта от външния клас невидимо се предава на създадения обект от вътрешния клас. В крайна сметка наличието на такава референция към обект е абсолютно изискване. В противен случай няма да можем да създаваме обекти от вътрешния клас.

    Но ако метод от външния клас е статичен, тогава може да нямаме обект от външния клас! И това би било нарушение на логиката на функционирането на една вътрешна класа. В тази ситуация компилаторът ще генерира грешка:

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. Вътрешен клас не може да съдържа статични променливи и методи.

    Логиката е същата: статичните методи и променливи могат да съществуват и да бъдат извиквани or препращани дори при липса на обект.

    Но без обект от външния клас, няма да имаме достъп до вътрешния клас.

    Явно противоречие! Ето защо статичните променливи и методи не са разрешени във вътрешните класове.

    Компилаторът ще генерира грешка, ако се опитате да ги създадете:

    
    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. Когато създавате обект от вътрешен клас, неговият модификатор за достъп е важен.

    Вътрешен клас може да бъде маркиран със стандартните модификатори за достъп: public, private, protectedи package private.

    Защо това има meaning?

    Това засяга къде можем да създаваме екземпляри на вътрешния клас в нашата програма.

    Ако нашият Seatклас е деклариран като public, можем да създаваме Seatобекти във всеки друг клас. Единственото изискване е да съществува и обект от външния клас.

    Между другото, вече направихме това тук:

    
    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();
       }
    }
    

    Лесно получихме достъп до Handlebarвътрешния клас от Mainкласа.

    Ако декларираме вътрешния клас като private, ще можем да създаваме обекти само във външния клас.

    Вече не можем да създадем Seatобект "отвън":

    
    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();
       }
    }
    

    Сигурно вече разбирате логиката :)

  6. Модификаторите за достъп за вътрешните класове работят по същия начин като за обикновените променливи.

    Модификаторът protectedосигурява достъп до променлива на екземпляр в подкласове и класове, които са в същия пакет.

    protectedработи и за вътрешни класове. Можем да създаваме protectedобекти от вътрешния клас:

    • във външния клас;
    • в неговите подкласове;
    • в класове, които са в същия пакет.

    Ако вътрешният клас няма модификатор за достъп ( package private), могат да се създават обекти от вътрешния клас:

    • във външния клас;
    • в класове, които са в същия пакет.

    Вие сте запознати с модификаторите от дълго време, така че тук няма проблеми.

Това е всичко за сега :) Но не се отпускайте! Вътрешните класове са доста обширна тема, която ще продължим да изследваме в следващия урок. Сега можете да опресните паметта си за урока от нашия курс за вътрешни класове . И следващия път нека поговорим за статични вложени класове.