21

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

10

Какова основная разница между внутренним классом и статическим вложенным классом в Java? Влияет ли выбор одного из этих классов на проектирование или реализацию?

5 ответ(ов)

1

Я не думаю, что настоящая разница стала ясна из приведенных выше ответов.

Сначала уточним термины:

  • Вложенный класс — это класс, который содержится в другом классе на уровне исходного кода.
  • Вложенный класс является статическим, если вы объявляете его с модификатором static.
  • Нестатический вложенный класс называется внутренним классом (я буду использовать термин "нестатический вложенный класс").

Ответ Мартина правильный на данный момент. Однако реальный вопрос состоит в том, какова цель объявления вложенного класса статическим или нестатическим?

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

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

public class Container {
    public class Item {
        Object data;
        public Container getContainer() {
            return Container.this;
        }
        public Item(Object data) {
            super();
            this.data = data;
        }
    }
    
    public static Item create(Object data) {
        // не компилируется, так как экземпляр Container недоступен
        return new Item(data);
    }
    
    public Item createSubItem(Object data) {
        // компилируется, так как 'this' (текущий) Container доступен
        return new Item(data);
    }
}

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

Чтобы объяснить более глубоко:

Если взглянуть на байт-коды Java, которые компилятор генерирует для (нестатического) вложенного класса, это может стать еще более ясным:

// версия класса 49.0 (49)
// флаги доступа 33
public class Container$Item {

  // скомпилирован из: Container.java
  // флаги доступа 1
  public INNERCLASS Container$Item Container Item

  // флаги доступа 0
  Object data

  // флаги доступа 4112
  final Container this$0

  // флаги доступа 1
  public getContainer() : Container
   L0
    LINENUMBER 7 L0
    ALOAD 0: this
    GETFIELD Container$Item.this$0 : Container
    ARETURN
   L1
    LOCALVARIABLE this Container$Item L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // флаги доступа 1
  public <init>(Container,Object) : void
   L0
    LINENUMBER 12 L0
    ALOAD 0: this
    ALOAD 1
    PUTFIELD Container$Item.this$0 : Container
   L1
    LINENUMBER 10 L1
    ALOAD 0: this
    INVOKESPECIAL Object.<init>() : void
   L2
    LINENUMBER 11 L2
    ALOAD 0: this
    ALOAD 2: data
    PUTFIELD Container$Item.data : Object
    RETURN
   L3
    LOCALVARIABLE this Container$Item L0 L3 0
    LOCALVARIABLE data Object L0 L3 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Как видно, компилятор создает скрытое поле Container this$0. Оно устанавливается в конструкторе, который имеет дополнительный параметр типа Container для указания заключающего экземпляра. Этот параметр не виден в исходном коде, но компилятор имплицитно генерирует его для вложенного класса.

Пример Мартина

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

будет скомпилирован в вызов чего-то вроде (в байт-кодах)

new InnerClass(outerObject)

Для полноты картины:

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

1

Я думаю, что ни один из вышеуказанных ответов не объясняет вам реальную разницу между внутренним классом и статическим внутренним классом с точки зрения проектирования приложения:

Обзор

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

Разница

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

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

Заключение

Таким образом, основная разница между ними с точки зрения проектирования в том, что нестатический внутренний класс может получить доступ к экземпляру контейнерного класса, в то время как статический — нет.

0

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

Вложенные классы — это классы, определённые внутри тела другого класса-обёртки. Они бывают двух типов: статические и нестатические.

Вложенные классы рассматриваются как члены класса-обёртки, и поэтому вы можете использовать любые из четырёх спецификаторов доступа: private, package-private, protected и public. У верхнеуровневых классов нет такого выбора, они могут быть объявлены только как public или package-private.

Нестатические внутренние классы имеют доступ к другим членам верхнего класса, даже если они объявлены как private, в то время как статические вложенные классы не имеют доступа к другим членам верхнего класса.

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1 — это наш статический вложенный класс, а Inner2 — наш внутренний класс, который не является статическим. Ключевое различие между ними в том, что вы не можете создать экземпляр Inner2 без объекта внешнего класса, тогда как объект Inner1 можно создать независимо.

Когда стоит использовать внутренние классы?

Представьте ситуацию, когда Class A и Class B связаны, и Class B нужно получить доступ к членам Class A, причём Class B связана только с Class A. Здесь на помощь приходят внутренние классы.

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

OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();

или

OuterClass.Inner2 inner = new OuterClass().new Inner2();

Когда стоит использовать статический внутренний класс?

Статический внутренний класс следует определять, когда вы знаете, что он не имеет отношения к экземпляру внешнего класса. Если ваш внутренний класс не использует методы или поля внешнего класса, то это просто пустая трата места, поэтому сделайте его статическим.

Например, чтобы создать объект статического вложенного класса, используйте следующий синтаксис:

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

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

0

Согласно общепринятой конвенции, классы в Java можно классифицировать следующим образом:

  • Статический класс внутри топ-уровнего класса является вложенным классом.
  • Нестатический класс внутри топ-уровнего класса - это внутренний класс (inner class), который имеет две подкатегории:
    • локальный класс - именованный класс, объявленный внутри блока, такого как тело метода или конструктора.
    • анонимный класс - класс без имени, экземпляры которого создаются в выражениях и инструкциях.

Некоторые важные моменты для запоминания:

  • Топ-уровневые классы и статические вложенные классы семантически идентичны, за исключением того, что статический вложенный класс может делать статические ссылки на приватные статические поля/методы своего внешнего (родительского) класса и наоборот.

  • Внутренние классы имеют доступ к переменным экземпляра enclosing (внешнего) экземпляра родительского (outer) класса. Однако не все внутренние классы имеют экземпляры enclosing, например, внутренние классы в статическом контексте, как анонимный класс, используемый в статическом блоке инициализации, не имеют.

  • Анонимный класс по умолчанию расширяет родительский класс или реализует родительский интерфейс, при этом нет возможности расширять какие-либо другие классы или реализовывать дополнительные интерфейсы. То есть,

    • new YourClass(){}; значит class [Anonymous] extends YourClass {}
    • new YourInterface(){}; значит class [Anonymous] implements YourInterface {}

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

0

Вопрос: Чем отличаются вложенные классы (nested classes) в Java, а именно статические вложенные классы (static nested classes) и нестатические вложенные классы (inner classes)?

Ответ:

В Java существует два основных типа вложенных классов:

  1. Статические вложенные классы (Static nested class)
  2. Нестатические вложенные классы (Inner class)

Различия:

Нестатические вложенные классы (Inner class)

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

outerclass outerObject = new outerclass();
outerclass.innerclass innerObject = outerObject.new innerclass(); 

Статические вложенные классы (Static nested class)

Для статического вложенного класса объект не требует наличия объекта внешнего класса, поскольку слово "static" подразумевает, что создание объекта не требуется. Таким образом, можно использовать статический вложенный класс напрямую.

class outerclass {
    static class nestedclass {
        static int x = 10;
    }
}

Чтобы получить доступ к переменной x, вы можете сделать это внутри метода следующим образом:

System.out.println(outerclass.nestedclass.x);

Надеюсь, это помогло прояснить различия между статическими и нестатическими вложенными классами в Java!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь