Вложенные и внутренние классы

Как организовано наследование классов в Java? В чём отличие вложенных и внутренних классов? Что такое локальные и анонимные классы?
20 сентября 2017326451Илья Бубнов55717

Одним из фундаментальных принципов языка java является наследование и инкапсуляция. Проводником этой идеи выступают внутренние и вложенные классы. Чуть ниже мы разберёмся в их отличиях.

class OuterClass {
  ...
  class NestedClass {
    ...
  }
}

Показанный пример наглядно демонстрирует суть темы. Внутри классов могут размещаться подклассы, использующие свойства родителя, даже те, которые не доступны извне. Таким образом, можно выделить 3 основные причины использования в java вложенных и внутренних классов:

  • Читаемость. Вложения позволяют разработчику тратить меньше времени на анализ возможных ошибок, а стороннему программисту удобнее читать такой код.
  • Инкапсуляция. Вложенные java классы могут наследовать приватные свойства и быть при этом приватными. С внешним классом такое проделать невозможно.
  • Группировка. На практике часто есть один большой класс, свойства которого наследуются более мелкими. В этом случае имеет смысл их группировать не только для повышения читаемости, но и точки зрения логики построения кода.

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

Вернёмся к терминологии. Классы, объявляемые внутри другого класса, могут быть статическими и не статическими. Первые для удобства принято называть вложенными, вторые – внутренними. Пример создания вложенного класса:

class OuterClass {
  ...
  static class StaticNestedClass {
    ...
  }
}

Обращение к нему в теле программы:

OuterClass.StaticNestedClass

Для создания объекта необходимо написать следующее:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

В случае с java вложенными статическими классами вы не можете напрямую обращаться к методам родительского класса или нестатическим полям. Для решения  этой задачи необходимо создать экземпляр и работать со ссылкой. Данная конструкция схожа с тем, как осуществляется взаимодействие с классами извне.

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

Внутренние классы делятся на 3 группы:

  • классы-члены;
  • анонимные классы;
  • локальные классы.

Первая группа фактически не имеет никаких ограничений, может обращаться ко всем переменным и методам старшего класса.

class Outer {
  int x1 = 3;
  class Inner {
    void display() {
      System.out.println ("x1 = " + String.valueOf(x1));
    }
  }
}

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

Локальные классы необходимо объявлять внутри методов, вне их использовать их нельзя. Кроме того, они могут обращаться ко всем внешним переменным, но только если те являются final.

public void print() {
//локальный внутренний класс внутри метода
  class Logger {
    String name;
  }
//пример использования локального внутреннего класса внутри метода
Logger logger = new Logger();
}

Локальные классы можно объявлять в циклах, операторах if, методах, но надо помнить об ограничениях:

  • видимости только внутри блока;
  • локальные классы не могут быть private, public, protected или static;
  • внутри них только константы могут быть статическими (static final). Статические методы и поля не допускаются.

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

Анонимные внутренние классы java аналогичны локальным, только не имеют имени. Они также имеет все указанные ограничения, плюс для них нельзя создать конструктор. Перенесём всё сказанное код, и покажем пример анонимного java класса:

class Clazz {
  Clazz(int param) { }

  public static void main(String[] args) {
    new Clazz(1) { }; // правильное создание анонимного класса
    new Clazz() { }; // неправильное создание анонимного класса
  }
}

Анонимные классы позволяют разработчику экономить время:

  • на создании, так как это короткая конструкция, не имеющая даже имени;
  • на чтении кода, так как область его действия сильно ограничена.

В целом, использование вложенных и внутренних классов – мера по увеличению качества кода. Он становится понятнее, безопаснее, удобнее для чтения и внесения изменений.