Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Вложенные классы

Класс можно объявить внутри другого класса. Так поступают с небольшими вспомогательными классами, которые нужны только своему «хозяину». Вложение показывает связь между классами и не засоряет программу лишними именами.

Статический вложенный класс

Класс с модификатором static внутри другого класса — это обычный класс, просто его полное имя включает имя внешнего: Order.Item. С объектами внешнего класса он не связан:

class Order {
    static class Item {
        String title;
        int quantity;
 
        Item(String title, int quantity) {
            this.title = title;
            this.quantity = quantity;
        }
    }
}
 
void main() {
    Order.Item item = new Order.Item("Чай", 2);
    IO.println(item.title + " x" + item.quantity);
}

Результат:

Чай x2

Внутренний класс

Вложенный класс без static называется внутренним. Каждый его объект привязан к объекту внешнего класса и видит его поля, даже закрытые:

class Counter {
    private int count;
 
    class Clicker {
        void click() {
            count++;
        }
    }
 
    Clicker newClicker() {
        return new Clicker();
    }
 
    int getCount() {
        return count;
    }
}
 
void main() {
    Counter counter = new Counter();
    Counter.Clicker clicker = counter.newClicker();
    clicker.click();
    clicker.click();
    IO.println(counter.getCount());
}

Результат:

2

Объект Clicker увеличивает счётчик именно того объекта Counter, которым была создана.

Локальный класс

Класс можно объявить прямо внутри метода. Он виден только в этом методе — удобно, когда вспомогательный тип нужен в одном месте. Чаще всего так объявляют локальную запись:

void main() {
    record Score(String name, int points) {}
 
    Score[] scores = { new Score("Анна", 90), new Score("Олег", 75) };
    for (Score s : scores) {
        IO.println(s.name() + ": " + s.points());
    }
}

Результат:

Анна: 90
Олег: 75

Анонимный класс

Анонимный класс объявляется и создаётся в одном выражении, без имени. Обычно так на ходу реализуют интерфейс:

interface Greeter {
    String greet(String name);
}
 
void main() {
    Greeter polite = new Greeter() {
        @Override
        public String greet(String name) {
            return "Добрый день, " + name + "!";
        }
    };
    IO.println(polite.greet("Анна"));
}

Результат:

Добрый день, Анна!

Если в интерфейсе один абстрактный метод, вместо анонимного класса обычно пишут лямбду: Greeter polite = name -> "Добрый день, " + name + "!";.

Какой выбрать

Начинайте со статического вложенного класса: он проще и не держит ссылку на внешний объект. Внутренний нужен, только если классу действительно нужны поля внешнего объекта. Кстати, все классы в примерах этой главы — тоже вложенные: компилятор помещает их внутрь неявного класса Main.

Частая ошибка

Объект внутреннего класса не может существовать без внешнего объекта. Попытка создать его как статический вложенный не компилируется:

class Counter {
    private int count;
 
    class Clicker {
        void click() {
            count++;
        }
    }
}
 
void main() {
    Counter.Clicker clicker = new Counter.Clicker();
}

Результат:

Main.java:12: error: an enclosing instance that contains Main.Counter.Clicker is required
    Counter.Clicker clicker = new Counter.Clicker();
                              ^

Создайте объект через внешний: counter.new Clicker(), или сделайте класс static, если поля внешнего объекта ему не нужны.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.