Вложенные (nested) классы в Java бывают двух видов: статические и нестатические. Если внутри одного класса объявить другой с модификатором static, то такой класс называется статически вложенным (static nested). Если просто объявить класс внутри другого, то такой класс называется внутренним (inner).
Статически вложенные классы (static nested)
1 2 3 4 5 6 |
public class OuterClass { ... static class InnerClass { ... } } |
Статически вложенный класс в общем и целом ведёт себя как обычный невложенный класс, как если бы OuterClass и InnerClass лежали в одном пакете. Основное отличие от обычных невложенных классах в том, как содаётся экземпляр такого класса:
1 |
OuterClass.InnerClass staticNestedClassInstance = new OuterClass.InnerClass(); |
То есть и тип переменной и вызов new для такого класса всегда помимо имени вложенного класса содержит и имя объемлющего.
Обычно такие классы создают, чтобы не плодить лишних файлов, когда точно известно, что вложенный класс всегда будет создаваться параллельно с объемлющим. Например, у вас есть класс Книга и класс Страница. Наверняка Страницы будут создаваться вместе с книгой и, возможно, логичней будет держать класс Страницы не в отдельном файле, а вложить в класс Книга.
Внутренние классы (inner)
1 2 3 4 5 6 |
public class OuterClass { ... class InnerClass { ... } } |
Внутренний класс существует только в контексте объекта объемлющего класса. Таким образом сначала создаётся объект OuterClass, а затем с помощью специального синтаксиса объект InnerClass:
1 2 |
OuterClass outerObject = new OuterClass() OuterClass.InnerClass innerObject = outerObject.new InnerClass(); |
Как видно, тип переменной внутреннего класса также указывается через точку после имени объемлющего класса: OuterClass.InnerClass. Но оператор new используется необычным образом: outerObject.new InnerClass(). Так как новый объект внутреннего класса InnerClass существует в контексте конкретного объекта outerObject. Можно сделать другой объект типа OuterClass и в контексте этого другого объекта создать другой объект вложенного класса.
Объекты внутреннего класса редко используются за пределами объемлющего. То есть, хотя выше показано, как создать такой объект в любой точке кода, в реальности этим пользуются крайне редко. В подавляющем большинстве случаев объекты внутреннего класса используются внутри методов объемлющего, и синтаксис там намного проще:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class Passport { class FullName { private String name; private String lastName; String toLine() { return name + " " + lastName; } } FullName fullName; Date birthDate; public String printPassportData() { return "Name: " + fullName.toLine() + ", birth date: " + birthDate; } public Passport() { this.fullName = new FullName(); this.fullName.name = "Ivan"; this.fullName.lastName = "Ivanov"; this.birthDate = new Date(0L); } public static void main(String[] args) { Passport passport = new Passport(); System.out.println(passport.printPassportData()); } } |
Обратите внимание, в конструкторе объемлющего класса экземпляр вложенного создаётся самым обычным способом: this.fullName = new FullName();
Нам оказалось удобно внутри класса Passport объединить несколько полей (name и lastName) во внутренний класс FullName с собственными методами. Мы это сделали и внутри объемлющего класса просто оперируем переменной FullName fullName, а не полями name и lastName