JavaRush /Курси /JAVA 25 SELF /Статичні вкладені класи (static nested)

Статичні вкладені класи (static nested)

JAVA 25 SELF
Рівень 16 , Лекція 1
Відкрита

1. Статичний вкладений клас

Статичний вкладений клас (static nested class) — це клас, оголошений усередині іншого класу з модифікатором static. По суті, це звичайний клас, який просто «живе» всередині іншого класу, але не повʼязаний із його обʼєктом.

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

Ключові відмінності:

  • Не має неявного посилання на обʼєкт зовнішнього класу — жодного OuterClass.this і жодного доступу до нестатичних членів.
  • Може містити статичні члени (на відміну від звичайного внутрішнього класу).
  • Створюється без обʼєкта зовнішнього класу.

Синтаксис оголошення

Оголосити статичний вкладений клас дуже просто: використовуємо ключове слово static всередині зовнішнього класу.

class Outer {
    static class Nested {
        void print() {
            System.out.println("Hello from Nested!");
        }
    }
}

Ось і все! Ніяких хитрих синтаксичних конструкцій — просто static class.

Візуалізація:

Outer (зовнішній клас)
│
├── Nested (static nested class)
│      └── print()

2. Створення екземпляра статичного вкладеного класу

Найприємніше: не потрібен обʼєкт зовнішнього класу!

Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!

Зверніть увагу: ми використовуємо повну кваліфіковану назву класу — Outer.Nested. Це як звертатися до вкладеного класу через прізвище: «Сміт.Син».

Порівняйте з внутрішнім (inner) класом:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // потрібен об’єкт outer

А для static nested — обʼєкт Outer взагалі не потрібен!

3. Доступ до членів зовнішнього класу

Саме тут криється головна відмінність від внутрішнього класу.

  • Статичний вкладений клас має доступ лише до статичних членів зовнішнього класу.
  • До нестатичних полів і методів звертатися не можна (навіть якщо вони public).

Приклад:

class Outer {
    private static int staticValue = 10;
    private int instanceValue = 20;

    static class Nested {
        void show() {
            System.out.println("Static value: " + staticValue); // OK
            // System.out.println("Instance value: " + instanceValue); // Помилка!
        }
    }
}

Якщо спробувати звернутися до нестатичного поля instanceValue, компілятор відразу видасть помилку.

Чому так? Тому що static nested class не «знає», з яким обʼєктом Outer його повʼязати — у нього немає посилання на обʼєкт зовнішнього класу.

4. Коли використовувати статичні вкладені класи

Коли вони доречні?

  • Коли вкладений клас логічно повʼязаний із зовнішнім, але не потребує доступу до обʼєкта зовнішнього класу.
  • Коли хочете інкапсулювати допоміжну структуру: наприклад, builder, утиліту, перелік (enum) або навіть невеликий immutable‑обʼєкт.
  • Коли потрібно зменшити «засмічення» пакета: клас потрібен лише зовнішньому класу, немає сенсу виносити його назовні.

Типові сценарії:

  • Патерн Builder (особливо для immutable‑обʼєктів)
  • Реалізація допоміжних структур: наприклад, внутрішні класи Node у колекціях
  • Групування констант або утиліт

Просте правило вибору
Поставте собі запитання: «Чи потрібен моєму вкладеному класу доступ до конкретного обʼєкта зовнішнього класу?»
НІ → використовуйте static class (статичний вкладений)
ТАК → використовуйте звичайний class (внутрішній)

5. Приклади використання

Приклад 1: Builder для класу

Припустімо, у нас є клас Person, і ми хочемо реалізувати для нього патерн Builder:

public class Person {
    private final String name;
    private final int age;

    // Приватний конструктор
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Статичний вкладений клас Builder
    public static class Builder {
        private String name;
        private int age;

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

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

        public Person build() {
            return new Person(this);
        }
    }

    public void printInfo() {
        System.out.println("Person: " + name + ", " + age);
    }
}

Використання:

Person person = new Person.Builder()
    .setName("Іван")
    .setAge(30)
    .build();

person.printInfo(); // Person: Іван, 30

Чому Builder — статичний вкладений клас?
Тому що він не залежить від обʼєкта Person, а лише допомагає його створити. Він логічно повʼязаний із Person, але не з конкретним екземпляром.

Приклад 2: Допоміжна структура всередині колекції

Уявімо клас «коробка з числами», де для зберігання елементів використовується вкладений статичний клас Node:

public class IntBox {
    private Node head;

    // Вкладений статичний клас
    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
        }
    }

    public void add(int value) {
        Node node = new Node(value);
        node.next = head;
        head = node;
    }

    public void printAll() {
        Node current = head;
        while (current != null) {
            System.out.println(current.value);
            current = current.next;
        }
    }
}

Використання:

IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1

Чому Node — статичний?
Тому що кожен Node не має знати про всю коробку (IntBox), він просто зберігає дані та посилання на наступний Node.

Приклад 3: Утилітний клас усередині основного

public class MathUtils {
    // Статичний вкладений клас для роботи з комплексними числами
    public static class Complex {
        private final double re;
        private final double im;

        public Complex(double re, double im) {
            this.re = re;
            this.im = im;
        }

        public Complex add(Complex other) {
            return new Complex(this.re + other.re, this.im + other.im);
        }

        @Override
        public String toString() {
            return re + " + " + im + "i";
        }
    }
}

Використання:

MathUtils.Complex a = new MathUtils.Complex(1, 2);
MathUtils.Complex b = new MathUtils.Complex(3, 4);
MathUtils.Complex sum = a.add(b);
System.out.println(sum); // 4.0 + 6.0i

6. Корисні нюанси

Внутрішній клас vs статичний вкладений клас

Внутрішній (inner) клас Статичний вкладений (static nested)
Ключове слово ні
static
Неявне посилання на зовнішній обʼєкт так ні
Доступ до нестатичних членів зовнішнього класу так ні
Доступ до статичних членів зовнішнього класу так так
Може містити статичні члени ні (лише константи) так
Синтаксис створення
outer.new Inner()
new Outer.Nested()
Використання Коли потрібен доступ до обʼєкта зовнішнього класу Коли доступ до обʼєкта зовнішнього класу не потрібен

Ілюстрація

flowchart LR
    OuterClass -->|has| InnerClass
    OuterClass -.->|has| StaticNestedClass
    StaticNestedClass -.->|can access| staticMembers
    InnerClass -->|can access| instanceMembers
    InnerClass -->|can access| staticMembers

Особливості та обмеження

  • Static nested class може містити як звичайні, так і статичні поля та методи.
  • Може бути оголошений із будь-яким модифікатором доступу (public, private, protected, package-private).
  • Може реалізовувати інтерфейси та наслідувати від інших класів.
  • Може бути generic.
  • Зазвичай використовується для інкапсуляції службових класів, які не потрібні поза межами зовнішнього класу.

Приклад generic static nested class:

public class Box {
    public static class Holder<T> {
        private T value;
        public Holder(T value) { this.value = value; }
        public T get() { return value; }
    }
}

Коли НЕ варто використовувати static nested class

  • Якщо вкладеному класу потрібен доступ до нестатичних полів/методів зовнішнього класу — використовуйте звичайний inner class.
  • Якщо клас потрібен поза межами зовнішнього класу — винесіть його в окремий файл.
  • Якщо клас занадто великий або складний — краще зробити його окремим.

7. Типові помилки та нюанси

Помилка № 1: Плутанина між inner і static nested класом.
Багато новачків намагаються у static nested class звертатися до нестатичних полів зовнішнього класу. Але static nested class не має посилання на обʼєкт зовнішнього класу, тож це неможливо. Якщо вам потрібен доступ до стану конкретного обʼєкта — використовуйте звичайний inner class.

Помилка № 2: Спроба створити static nested class через обʼєкт зовнішнього класу.
Немає потреби писати outer.new Inner(). Для static nested class завжди використовуйте new Outer.Nested().

Помилка № 3: Використання static nested class для логіки, що потребує доступу до екземпляра зовнішнього класу.
Якщо логіка класу тісно повʼязана зі станом обʼєкта зовнішнього класу, static nested class — поганий вибір. Використовуйте звичайний внутрішній клас.

Помилка № 4: Надто складна вкладеність.
Не зловживайте вкладеними класами. Якщо структура стає заплутаною, краще винести частину класів назовні.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ