Перейти к основному содержимому

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

· 5 мин. чтения

1. Введение

Этот учебник представляет собой краткое и точное введение во вложенные классы в языке Java.

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

Прежде чем мы начнем, давайте рассмотрим несколько типов вложенных классов, доступных в языке:

  • Статические вложенные классы
  • Нестатические вложенные классы
  • Местные классы
  • Анонимные классы

В следующих разделах мы подробно обсудим каждый из них.

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

Вот несколько моментов, которые следует помнить о статических вложенных классах:

  • Как и в случае статических членов, они принадлежат окружающему их классу, а не экземпляру класса.
  • Они могут иметь все типы модификаторов доступа в своем объявлении.
  • У них есть доступ только к статическим членам окружающего класса.
  • Они могут определять как статические, так и нестатические элементы.

Давайте посмотрим, как мы можем объявить статический вложенный класс:

public class Enclosing {

private static int x = 1;

public static class StaticNested {

private void run() {
// method implementation
}
}

@Test
public void test() {
Enclosing.StaticNested nested = new Enclosing.StaticNested();
nested.run();
}
}

3. Нестатические вложенные классы

Далее, вот несколько моментов, которые следует помнить о нестатических вложенных классах:

  • Их также называют внутренними классами.
  • Они могут иметь все типы модификаторов доступа в своем объявлении.
  • Так же, как переменные и методы экземпляра, внутренние классы связаны с экземпляром окружающего класса.
  • У них есть доступ ко всем членам окружающего класса, независимо от того, являются ли они статическими или нестатическими.
  • Они могут определять только нестатические члены

Вот как мы можем объявить внутренний класс:

public class Outer {

public class Inner {
// ...
}
}

Если мы объявим вложенный класс с модификатором static , то это статический член. В противном случае это внутренний класс. Несмотря на то, что синтаксически разница заключается всего в одном ключевом слове (т . е. static ), семантически существует огромная разница между этими типами вложенных классов. Экземпляры внутреннего класса привязаны к экземплярам окружающего класса и поэтому имеют доступ к своим членам. Мы должны помнить об этой проблеме, когда выбираем, следует ли сделать вложенный класс внутренним.

Чтобы создать экземпляр внутреннего класса, мы должны сначала создать экземпляр его внешнего класса.

Давайте посмотрим, как мы можем это сделать:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

В следующих подразделах мы собираемся показать некоторые специальные типы внутренних классов.

3.1. Местные классы

Локальные классы — это особый тип внутренних классов, в которых класс определяется внутри блока метода или области видимости.

Давайте рассмотрим несколько моментов, которые следует помнить об этом типе класса:

  • Они не могут иметь модификаторы доступа в своем объявлении.
  • У них есть доступ как к статическим, так и к нестатическим членам во внешнем контексте.
  • Они могут определять только члены экземпляра

Вот краткий пример:

public class NewEnclosing {

void run() {
class Local {

void run() {
// method implementation
}
}
Local local = new Local();
local.run();
}

@Test
public void test() {
NewEnclosing newEnclosing = new NewEnclosing();
newEnclosing.run();
}
}

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

Анонимные классы можно использовать для определения реализации интерфейса или абстрактного класса без необходимости создания повторно используемой реализации.

Давайте перечислим несколько моментов, которые следует помнить об анонимных классах:

  • Они не могут иметь модификаторы доступа в своем объявлении.
  • У них есть доступ как к статическим, так и к нестатическим членам во внешнем контексте.
  • Они могут определять только члены экземпляра
  • Это единственный тип вложенных классов, которые не могут определять конструкторы или расширять/реализовывать другие классы или интерфейсы.

Чтобы определить анонимный класс, давайте сначала определим простой абстрактный класс:

abstract class SimpleAbstractClass {
abstract void run();
}

Теперь давайте посмотрим, как мы можем определить анонимный класс:

public class AnonymousInnerUnitTest {

@Test
public void whenRunAnonymousClass_thenCorrect() {
SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() {
void run() {
// method implementation
}
};
simpleAbstractClass.run();
}
}

Для получения более подробной информации нам может пригодиться наш учебник по анонимным классам в Java .

4. Затенение

Объявление членов внутреннего класса затеняет членов окружающего класса , если они имеют одно и то же имя.

В этом случае ключевое слово this относится к экземплярам вложенного класса, а к членам внешнего класса можно обращаться, используя имя внешнего класса.

Давайте посмотрим на быстрый пример:

public class NewOuter {

int a = 1;
static int b = 2;

public class InnerClass {
int a = 3;
static final int b = 4;

public void run() {
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("NewOuterTest.this.a = " + NewOuter.this.a);
System.out.println("NewOuterTest.b = " + NewOuter.b);
System.out.println("NewOuterTest.this.b = " + NewOuter.this.b);
}
}

@Test
public void test() {
NewOuter outer = new NewOuter();
NewOuter.InnerClass inner = outer.new InnerClass();
inner.run();

}
}

5. Сериализация

Чтобы избежать java.io.NotSerializableException при попытке сериализации вложенного класса, мы должны:

  • Объявите вложенный класс как статический
  • Сделайте так, чтобы вложенный класс и включающий класс реализовывали Serializable .

6. Заключение

В этой статье мы увидели, что такое вложенные классы и их различные типы. Мы также рассмотрели, как различаются модификаторы видимости полей и доступа для разных типов.

Как всегда, полную реализацию этого туториала можно найти на GitHub .