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

142

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

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

Рахул Гарг
источник
4
Потому что теперь Java - это тот старый КОБОЛ :)
ses
Суть в следующем: потому что они еще не реализовали это.
intrepidis
1
«Нестатичное внутреннее» - тавтология.
Маркиз Лорн
Если вы не хотите показывать свой внутренний класс другим и надеетесь, что он содержит статические методы, вы можете поместить модификаторы «private» и «static» во внутренний класс.
Вилли З
Внутренний класс по определению не является статичным. Вы можете иметь статический вложенный класс / член и нестатический вложенный класс / член. Последний также известен как внутренний класс. (Ссылка: раздел 8.1.3 JLS гласит: « Внутренний класс - это вложенный класс, который не объявлен явно или неявно static.»)
Эрвин Болвидт

Ответы:

111

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

Билл Ящерица
источник
7
Я знаю, что внутренний класс связан с экземпляром его внешнего класса, и я знаю, что это бесполезно, что мы можем объявлять статические члены во внутреннем классе, но я все еще спрашиваю, почему внутренний класс не может объявлять статические члены?
Карим
15
В C ++ это возможно, так что это ошибка в языке Java.
Промышленный антидепрессант
39
Слово ошибка ... Я не думаю, что это слово означает то, что вы думаете, что это значит.
Сет Нельсон
24
Более подходящая фраза была бы «раздражающей, как прародительница». Не понимаю, почему Java не допускает этого. Иногда я хочу, чтобы внутренний класс использовал свойства родительского класса, но сохранял статические методы для лучшего пространства имен. Что-то не так с этим? :(
Ангад
6
Именно. Я хочу написать служебный внутренний класс. Некоторым из его методов будет полезен доступ к внешнему классу, поэтому я не могу сделать его статическим, но некоторые из его методов - просто служебные функции. Почему я не могу позвонить A.B.sync(X)или даже (изнутри А) B.sync(x)?
Эдвард Фальк
44

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

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

Outer.test();

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

Outer.Inner.innerTest();

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

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

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

Эдди
источник
6
-1 Я должен не согласиться с тем углом, который ты здесь взял. Конечно , можно сослаться тип внутреннего класса, например Outer.Inner i = new Outer().new Inner();Кроме того , внутренние классы будут разрешены , чтобы объявить статические константы в соответствии с JLS §15.28.
Пол Беллора
2
Да, внутренние классы могут объявлять статические константы . Это не имеет ничего общего со статическими методами! Хотя вы можете ссылаться на статический метод нестатически, это не рекомендуется. Все инструменты качества кода жалуются на такую ​​ссылку и не зря. И ты упустил мою точку зрения. Я никогда не говорил, что нет никакого способа сослаться на статический внутренний класс. Я сказал, что нет статического способа ссылки на статический метод внутреннего класса нестатического внешнего класса. Таким образом, нет правильного способа ссылки на него.
Эдди
25
«Нет особого смысла разрешать статический метод в нестатическом внутреннем классе; как бы вы получили к нему доступ?» Вы бы позвонили так Outer.Inner.staticMethod()же, как вы можете получить доступ Outer.Inner.CONSTANT. «Вы не можете получить доступ к ... нестатическому экземпляру внутреннего класса, не пройдя экземпляр внешнего класса». Зачем вам нужен экземпляр? Вам не нужен экземпляр Outerдля вызова Outer.staticMethod(). Я знаю, что это придирчиво, но я хочу сказать, что не имеет смысла формулировать ваш ответ таким образом. ИМХО, дизайнеры языка могли бы позволить это, если пожелают.
Пол Беллора
1
Разница между Outer.Inner.CONSTANTи Outer.Inner.staticMethod()заключается в том, что ссылка на константу не имеет возможности неявно ссылаться на экземпляр, Outerв котором Innerбыл создан экземпляр. Все ссылки Outer.staticMethod()имеют одинаковое точное состояние. Все ссылки Outer.Inner.CONSTANTимеют одинаковое точное состояние. Тем не менее, ссылки на Outer.Inner.staticMethod()неоднозначны: «статическое» состояние не является действительно статическим из-за неявной ссылки на внешний класс в каждом экземпляре Inner. Нет по-настоящему однозначного, статичного способа получить к нему доступ.
Эдди
3
@Eddie Нельзя ссылаться на поля экземпляра в статическом методе, поэтому нет конфликта, связанного с невозможностью ссылки на неявное поле экземпляра Outer.this. Я согласен с разработчиками языка Java в том, что нет оснований разрешать статические методы или неконечные статические поля во внутренних классах, потому что все во внутреннем классе должно быть в контексте окружающего класса.
Теодор Мердок
20

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

Во-первых, вы должны знать кое-что о том, как внутренние классы реализованы в 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как не является статичным. Такого рода объясняется, почему статические методы не разрешены (хотя вы можете указать, что вы можете разрешить статические методы, если они не ссылаются на включающий объект), и почему у вас не может быть не финальных статических полей ( было бы нелогично, если бы экземпляры нестатических внутренних классов из разных объектов разделяли «статическое состояние»). Это также объясняет , почему конечные поля являются разрешено ( до тех пор , пока они не ссылаться на объекте ограждающего).

gustafc
источник
7
Но это обычная ошибка типа «попытка доступа к нестатической переменной из статического контекста» - ничем не отличается от того, если статический метод верхнего уровня пытается получить доступ к переменной экземпляра своего класса.
Лоуренс Дол
2
Мне нравится этот ответ, потому что он на самом деле объясняет, почему это технически невозможно, хотя синтаксически это может показаться возможным.
LoPoBo
@ Gustafc, я думаю, это было отличное объяснение. Но, как указывает Лоуренс, это только ошибка из-за ссылки на foo, которая не является статичной. Но что если я захочу написать public static double sinDeg(double theta) { ... }внутренний класс математических утилит?
Эдвард Фальк
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Бенджамин Кертис Дрейк
источник
3

Короткий ответ: ментальная модель, которую имеет большинство программистов о том, как работает область, не является моделью, используемой 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. Это сбивает с толку, потому что нам обычно говорят о статической части класса как о другом уровне области видимости:

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

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

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

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

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

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

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

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

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

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

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

  1. Означает ли это, что в ВМ есть только один экземпляр?
  2. Или только один экземпляр на внешний объект?

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

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

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

akhil_mittal
источник
3

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

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

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

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

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

Doomed93
источник
2

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

БЫТЬ
источник
Бенедикт, что ты имеешь в виду, когда говоришь, что "статические вложенные классы являются лишь средством группировки"?
Ankur
0

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

малайский
источник
0

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

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

как ниже код,

public class Outer {

  class Inner {

    public static void method() {

    }

  }

}

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

public class Outer {

  void method() {

   }

   class Inner {


  }

}

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

Вишну Саини
источник
0

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

//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something 
user1888955
источник
-1

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

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

Creative_Cimmons
источник
-2

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

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
mR_fr0g
источник