1. Внутрішні класи

Нещодавно ви дізналися, що є статичні змінні й статичні методи, однак, виявляється, є ще й статичні класи. Але почнемо здаля.

У мові Java можна оголошувати класи всередині класів. І навіть класи всередині класів, які є всередині класів усередині класів. Вигляд такого оголошення дуже простий:

class ЗовнішнійКлас
{
   змінні класу
   методи класу

   class ВнутрішнійКлас
   {
      змінні класу
      методи класу
   }
}

Просто оголошуємо один клас усередині іншого — і готово.

Приклад:

public class Solution
{
   static ArrayList<Point> points = new ArrayList<Point>();

   public static void main(String[] args)
   {
      Point point = new Point();
      point.x = 100;
      point.y = 200;
      points.add(point);
   }

   static class Point
   {
      int x;
      int y;
   }
}

Внутрішні класи можуть бути статичними й нестатичними. Статичні класи ще називають вкладеними (static nested class), нестатичні називають просто внутрішніми класами (inner class).



2. Статичні класи

Статичні вкладені класи можна використовувати поза батьківським класом. Клас, який має модифікатор доступу public, можна використовувати в будь-якому місці програми. Такі класи майже нічим не відрізняються від будь-якого звичайного класу. Проте є кілька відмінностей.

Ім'я класу

Якщо потрібно звернутися до статичного вкладеного класу не з його батьківського класу, а з іншого місця в програмі, слід вказати ім'я класу: воно складається з імені батьківського класу та імені вкладеного класу. Загальний вигляд цього імені такий:

БатьківськийКлас.ВкладенийКлас

Приклади:

Батьківський клас Вкладений клас Повне ім'я вкладеного класу
com.javarush.Solution
Point
com.javarush.Solution.Point
java.util.Map
Entry
java.util.Map.Entry
java.util.Files
DirectoryStream
java.util.Files.DirectoryStream
java.nio.WindowsPath
Closeable
java.nio.WindowsPath.Closeable

Якщо вкладений клас має свій вкладений клас, їхні імена просто склеюють через крапку.

Типовий приклад вкладеного класу в JDK — це клас Entry всередині класу Map. Якщо ви хочете отримати множину пар елементів з об'єкта HashMap, то метод entrySet() поверне вам множину пар типу Set<Map.Entry>.

Власне, запис Map.Entry — це і є зовнішній і внутрішній класи.

Створення об'єкта

Створити об'єкт вкладеного статичного класу дуже легко. Дивіться як:

БатьківськийКлас.ВкладенийКлас ім'я = new БатьківськийКлас.ВкладенийКлас();

Усе так само, як зі звичайними класами, тільки ім'я подвійне.

Звернення до статичних методів

Якщо статичний клас має статичні методи, до них можна звертатися так само, як до статичних методів звичайних класів (тільки ім'я класу тепер подвійне).

БатьківськийКлас.ВкладенийКлас.статичнийМетод();

Звернення до статичних змінних

Звертатися до публічних статичних змінних вкладеного класу теж просто:

БатьківськийКлас.ВкладенийКлас.ім'яСтатичноїЗмінної;


3. Особливості статичних класів

Статичні внутрішні класи, по суті, не варто називати статичними. Вони поводяться так само, як і звичайні класи. Ніяких обмежень на звернення до них із нестатичних методів немає.

Якщо ви працюєте з внутрішнім статичним класом усередині його батьківського класу, то взагалі не помітите жодної різниці між цим внутрішнім класом і звичайним класом (не вкладеним і не статичним).

Приклад:

Внутрішній статичний клас Point Звичайний клас Point
public class Solution
{
   static ArrayList<Point> points;

   public static void main(String[] args)
   {
      Point point = new Point();
      point.x = 100;
      point.y = 200;
      points = new ArrayList<Point>();
      points.add(point);
   }

   static class Point
   {
      int x;
      int y;
   }
}
public class Solution
{
   static ArrayList<Point> points;

   public static void main(String[] args)
   {
      Point point = new Point();
      point.x = 100;
      point.y = 200;
      points = new ArrayList<Point>();
      points.add(point);
   }
}

class Point
{
   int x;
   int y;
}

Якщо якийсь вкладений статичний клас перенести з батьківського класу назовні, нічого не зміниться, крім того, що новий клас утратить здатність звертатися до приватних статичних (private static) змінних і методів колишнього батьківського класу.

Приклад:

Внутрішній статичний клас Point Звичайний клас Point
public class Solution
{
   private static ArrayList<Point> points;

   static class Point
   {
      int x;
      int y;

      public static void main(String[] args)
      {
         Point point = new Point();
         point.x = 100;
         point.y = 200;

         // працюватиме
         points = new ArrayList<Point>();
         points.add(point);
      }
   }
}
public class Solution
{
   private static ArrayList<Point> points;
}

class Point
{
   int x;
   int y;

   public static void main(String[] args)
   {
      Point point = new Point();
      point.x = 100;
      point.y = 200;

      // тут буде помилка
      points = new ArrayList<Point>();
      points.add(point);
   }
}

Метод main у звичайному класі Point не може звертатися до приватної статичної (private static) змінної points класу Solution!

Це, власне, і є основною відмінністю вкладеного статичного класу від звичайного класу. Методи вкладеного статичного класу мають доступ до всіх статичних змінних і методів свого батьківського класу, навіть якщо їх оголошено як private.

Ну а якщо подумати, то що тут дивного? Модифікатор private прямо вказує, що до змінних і методів, позначених цим модифікатором, можна звертатися тільки зсередини їхнього класу. Внутрішній статичний клас міститься всередині батьківського класу? Якщо так, тоді ніяких проблем: звертайтеся скільки завгодно.