Почему у нас нет статического метода в (нестационарном) внутреннем классе?

Почему мы не можем иметь статический метод в нестационарном внутреннем классе?

Если я создаю статический класс, он работает. Почему?

Ответ 1

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

Ответ 2

Нет смысла указывать статический метод в нестационарном внутреннем классе; как бы вы к нему обращались? Вы не можете получить доступ (по крайней мере на начальном этапе) нестатический экземпляр внутреннего класса без прохождения экземпляра внешнего класса. Существует не чисто статический способ создания нестатического внутреннего класса.

Для внешнего класса Outer вы можете получить доступ к статическому методу test() следующим образом:

Outer.test();

Для статического внутреннего класса Inner вы можете получить доступ к его статическому методу innerTest() следующим образом:

Outer.Inner.innerTest();

Однако, если Inner не является статическим, теперь нет чисто статического способа ссылки на метод innertest. Нестатические внутренние классы привязаны к конкретному экземпляру их внешнего класса. Функция отличается от константы тем, что ссылка на Outer.Inner.CONSTANT гарантируется однозначно так, что вызов функции Outer.Inner.staticFunction(); не является. Скажем, у вас есть Inner.staticFunction(), который вызывает getState(), который определен в Outer. Если вы попытаетесь вызвать эту статическую функцию, у вас теперь есть двусмысленная ссылка на класс Inner. То есть, на каком экземпляре внутреннего класса вы вызываете статическую функцию? Это важно. Смотрите, нет действительно статического способа ссылаться на этот статический метод из-за неявной ссылки на внешний объект.

Пол Беллора прав, что дизайнеры языка могли это позволить. Затем им следует тщательно запретить любой доступ к неявной ссылке на внешний класс в статических методах нестатического внутреннего класса. На данный момент, каково значение для этого внутреннего класса, если вы не можете ссылаться на внешний класс, кроме статически? И если статический доступ в порядке, то почему бы не объявить весь внутренний класс static? Если вы просто сделаете внутренний класс статическим, то у вас нет неявной ссылки на внешний класс, и у вас больше нет этой двусмысленности.

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

Ответ 3

У меня есть теория, которая может быть или не быть правильной.

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

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(); }
}

Когда вы скомпилируете его, сгенерированный байт-код будет выглядеть так, как если бы вы написали что-то вроде этого:

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(this); }
}

Теперь, если мы допустили статические методы в нестатических внутренних классах, вы можете сделать что-то вроде этого.

class Outer {
    private int foo = 0;
    class Inner {
        public static void incrFoo(){ foo++; }
    }
}

..., который выглядит довольно разумно, поскольку класс Inner, кажется, имеет одно воплощение за экземпляр Outer. Но, как мы видели выше, нестатические внутренние классы действительно являются просто синтаксическим сахаром для статических "внутренних" классов, поэтому последний пример будет приблизительно эквивалентен:

class Outer {
    private int foo = 0;
    static class Inner {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public static void incrFoo(){ this$0.foo++; }
    }
}

... что явно не сработает, так как this$0 нестационар. Это объясняет, почему статические методы не разрешены (хотя вы можете сделать аргумент, что вы можете разрешить статические методы, если они не ссылаются на окружающий объект), и почему у вас не может быть статических полей без конечных значений ( это было бы контр-интуитивно, если бы экземпляры нестатических внутренних классов из разных объектов разделяли "статическое состояние" ). Это также объясняет, почему разрешены конечные поля (если они не ссылаются на объект-оболочку).

Ответ 4

Единственная причина - "не обязательно", так зачем ее поддерживать?

Синтаксически нет оснований запрещать внутреннему классу статических членов. Хотя экземпляр Inner связан с экземпляром Outer, все равно можно использовать Outer.Inner.myStatic для ссылки на статический член Inner, если java решит это сделать.

Если вам нужно поделиться чем-то среди всех экземпляров Inner, вы можете просто поместить их в Outer как статические члены. Это не хуже, чем использование статических членов в Inner, где Outer все равно может получить доступ к любому частному члену Inner в любом случае (не улучшает инкапсуляцию).

Если вам нужно поделиться чем-то из всех экземпляров Inner, созданных одним объектом Outer, имеет смысл помещать их в класс Outer как обычные члены.

Я не согласен с мнением, что "статический вложенный класс в значительной степени является только классом верхнего уровня". Я считаю, что лучше рассматривать статический вложенный класс/внутренний класс как часть внешнего класса, потому что они могут обращаться к закрытым членам внешнего класса. А члены внешнего класса также являются "членами внутреннего класса". Поэтому нет необходимости поддерживать статический член во внутреннем классе. Обычный/статический член во внешнем классе будет достаточным.

Ответ 5

Короткий ответ: ментальная модель, которую большинство программистов имеет, как работает область действия, не является моделью, используемой javac. Согласование более интуитивной модели потребовало бы больших изменений в работе javac.

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

class Outer {
   int outID;

   class Inner {
      static int nextID;
      int id = nextID++;

      String getID() {
         return outID + ":" + id;
      }
   }
}

Рассмотрим, что происходит в getID(), когда я использую неквалифицированный идентификатор "outID". Область видимости этого идентификатора выглядит примерно так:

Outer -> Inner -> getID()

Здесь снова, потому что это как раз работает javac, "внешний" уровень области включает как статические, так и экземпляры Outer. Это сбивает с толку, потому что нам обычно говорят, что статическая часть класса рассматривается как еще один уровень области:

Outer static -> Outer instance -> instanceMethod()
         \----> staticMethod()

В этом способе думать об этом, конечно, staticMethod() может видеть только статические члены Outer. Но если это так, как работает javac, то ссылка на переменную экземпляра в статическом методе приведет к ошибке "имя не может быть разрешено". Что действительно происходит, так это то, что имя найдено в области видимости, но затем добавляется дополнительный уровень проверки и выясняется, что имя было объявлено в контексте экземпляра и ссылается на статический контекст.

Хорошо, как это относится к внутренним классам? Наивно, мы считаем, что нет внутренних причин, по которым внутренние классы не могут иметь статический масштаб, потому что мы представляем область действия, которая работает следующим образом:

Outer static -> Outer instance -> Inner instance -> getID()
         \------ Inner static ------^

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

Это не так, как работает javac - существует один уровень видимости как для статических, так и для экземпляров, а область всегда строго гнездится. Даже наследование реализуется путем копирования объявлений в подкласс, а не разветвления и поиска в области суперкласса.

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

Ответ 6

Почему мы не можем использовать статический метод в нестационарном внутреннем классе?

Примечание. Нестатический вложенный класс известен как внутренний класс, поэтому у вас нет non-static inner class как такового.

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

  • Означает ли это, что в VM есть только один экземпляр?
  • Или только один экземпляр для внешнего объекта?

Вот почему дизайнеры, вероятно, приняли решение вообще не заниматься этой проблемой.

Если я создаю статический класс, он работает. Почему?

Снова вы не можете сделать внутренний класс static, а можете объявить статический класс вложенным. В этом случае этот вложенный класс фактически является частью внешнего класса и может содержать статические элементы без каких-либо проблем.

Ответ 7

Эта тема привлекла внимание многих, но я попытаюсь объяснить в самых простых терминах.

Во-первых, ссылаясь на http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1, класс или интерфейс инициализируются непосредственно перед первым вступлением/вызовом любого члена, который является предшествовало ключевое слово static.

  • Итак, если мы ставим статический член внутри внутреннего класса, это приведет к инициализации внутреннего класса, не обязательно внешнего/охватывающего класса. Таким образом, мы препятствуем последовательности инициализации класса.

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

Упрощая точку, для доступа к статическому элементу нам нужен экземпляр класса Outer, из которого нам снова понадобится создать экземпляр нестатического внутреннего класса. Статические члены не должны привязываться к экземплярам, ​​и поэтому вы получаете ошибку компиляции.

Ответ 8

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

Ответ 9

От: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

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

Объяснение Оракула поверхностное и сложное. Поскольку не было технической или синтаксической причины для вытеснения статических членов внутри внутреннего класса (это допускалось в других языках, таких как С#), мотивация разработчиков Java была, скорее всего, концептуальным вкусом и/или вопросом технического удобства.

Вот мое предположение:

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

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

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

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

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

Ответ 10

Предположим, что есть два экземпляра внешнего класса, и оба они имеют экземпляр внутреннего класса. Теперь, если внутренний класс имеет один статический член, тогда он сохранит только одну копию этого члена в области кучи. В этом случае оба объекта внешнего класса будут обратитесь к этой единственной копии, и они могут изменить ее вместе. Это может привести к ситуации с "грязным чтением", чтобы предотвратить, что эта Java применила это ограничение. Другой сильной стороной для поддержки этого аргумента является то, что java позволяет конечным статическим членам здесь, те, чьи значения могут 't быть измененным от любого из внешнего объекта класса. Пожалуйста, позвольте мне, если я ошибаюсь.

Ответ 11

Прежде всего, почему кто-то хочет определить статический член в нестационарном внутреннем классе? ответ, так что внешний член класса может использовать этот статический член только с внутренним именем класса, Right?

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

как показано ниже,

public class Outer {

  class Inner {

    public static void method() {

    }

  }

}

можно записать так:

public class Outer {

  void method() {

   }

   class Inner {


  }

}

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

Ответ 12

Попытайтесь рассматривать класс как нормальное поле, тогда вы поймете.

//something must be static. Suppose something is an inner class, then it has static keyword which means it a static class
Outer.something 

Ответ 13

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

Подумайте об этом, чтобы получить доступ к статическому члену, который вы используете className.memberName, в нашем случае это должно быть что-то вроде outerclassName.innerclassName.memberName,, теперь вы видите, почему innerclass должен быть статическим....

Ответ 14

Разрешены статические методы для статических вложенных классов. Например

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}