WedX - журнал о программировании и компьютерных науках

Почему Java запрещает статические поля во внутренних классах?

class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

Хотя невозможно получить доступ к статическому полю с помощью OuterClass.InnerClass.i, если я хочу записать что-то, что должно быть статическим, например количество созданных объектов InnerClass, было бы полезно сделать это поле статическим. Итак, почему Java запрещает статические поля / методы во внутренних классах?

РЕДАКТИРОВАТЬ: Я знаю, как сделать компилятор довольным статическим вложенным классом (или статическим внутренним классом), но я хочу знать, почему java запрещает статические поля / методы внутри внутренних классов (или обычного внутреннего класса) как из языковой конструкции, так и из аспекты реализации, если кто-то знает об этом больше.


  • Мой любимый пример - это Logger только для внутреннего класса. Он не может быть статичным, как все другие регистраторы. 06.11.2013
  • Начиная с Java 16, это уже не так - см. этот ответ. 04.12.2020

Ответы:


1

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

8.1.2 Внутренние классы и закрывающие экземпляры

Внутренний класс - это вложенный класс, который явно или неявно не объявлен статическим. Внутренние классы не могут объявлять статические инициализаторы (§8.7) или интерфейсы-члены. Внутренние классы не могут объявлять статические члены, если только они не являются полями констант времени компиляции (§15.28).

23.12.2009
  • может это просто так решено 23.12.2009
  • вы не можете создать экземпляр нестатического внутреннего без родительской ссылки, но вы все равно можете инициализировать его. 23.12.2009
  • Если загрузчики классов хранят кеш, в котором говорится, что класс X был инициализирован, то их логика не может использоваться для инициализации нескольких экземпляров класса [объектов, представляющих] X (что необходимо, когда объекты класса должны быть созданы как внутренние классы внутри нескольких отдельных объектов. ). 22.09.2015
  • @skaffman Это все еще не имеет смысла. Статические свойства внутреннего класса инициализируются только один раз, так в чем же проблема? Прямо сейчас у меня есть статическая хэш-карта, и у меня есть около 4 методов, которые управляют только этой картой, что делает более идеальным группировку всего во внутреннем классе. Однако статическая хэш-карта теперь должна будет жить снаружи и, возможно, с другими связанными с ней вещами, что просто глупо. В чем будет проблема с инициализацией статических свойств? 01.04.2016
  • Начиная с Java 16, это уже не так - см. этот ответ. 04.12.2020

  • 2

    я хочу знать, почему java запрещает статические поля / методы внутри внутренних классов

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

    Поскольку они являются «экземпляровыми» классами, нет никакого смысла разрешать static функции, поскольку static изначально предназначен для работы без экземпляра.

    Это как если бы вы одновременно пытались создать статический / экземплярный атрибут.

    Возьмем следующий пример:

    class Employee {
        public String name;
    }
    

    Если вы создаете два экземпляра сотрудника:

    Employee a = new Employee(); 
    a.name = "Oscar";
    
    Employee b = new Employee();
    b.name = "jcyang";
    

    Понятно, почему у каждого свое значение свойства name, верно?

    То же самое происходит с внутренним классом; каждый экземпляр внутреннего класса не зависит от другого экземпляра внутреннего класса.

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

    class Employee {
        public String name;
        class InnerData {
            static count; // ??? count of which ? a or b? 
         }
    }
    

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

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

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

    23.12.2009
  • Я куплю ваше объяснение статических свойств внутреннего класса, но, как указывает @skaffman в комментарии к моему ответу, как насчет статических методов? Кажется, что методы должны быть разрешены, не требуя их отсоединения от какого-либо экземпляра. Действительно, в java вы можете вызывать статические методы для экземпляров (хотя это считается плохим стилем). Кстати: я попросил коллегу попытаться скомпилировать код OP как C #, и он компилируется. Итак, C #, по-видимому, допускает это, что демонстрирует, что то, что хочет сделать OP, не нарушает какой-то фундаментальный принцип объектно-ориентированного программирования. 23.12.2009
  • Точно так же происходит и с методами. Дело здесь не в том, что атрибуты или методы являются статистическими или нет, а в том, что внутренний класс сам является материалом instance. Я имею в виду, проблема здесь в том, что экземпляр такого внутреннего класса не существует, пока не будет создан внешний класс. Следовательно, какой метод будет отправлен тогда, если ничего нет. Вы попадете в воздух только потому, что вам в первую очередь понадобится экземпляр. 23.12.2009
  • Насчет C # ... ну. Это не объектно-ориентированный подход только потому, что C # это допускает, я не имею в виду, что это неправильно, но C # включает несколько парадигм, упрощающих разработку даже за счет согласованности (вы должны изучать новые вещи с каждой версией .NET), и это позволяет, среди прочего, это и другие вещи. Я думаю, это хорошо. Если сообщество сочтет, что дополнительная функция - это достаточно круто, в C # она может появиться в будущем. 23.12.2009
  • @OscarRyz Зачем вам нужны экземпляры внутреннего класса для использования его статических методов / полей? И примером [полезного] статического метода во внутреннем классе является частный вспомогательный метод. 04.12.2014
  • При использовании final статические поля разрешены во внутреннем классе в java. Как вы объясните этот сценарий? 09.07.2018
  • Как объясняется в ответе, когда вы используете static int x; внутри внутреннего класса и объявляете два экземпляра внешнего класса, тогда как вы можете сказать, что является правильным значением x? Поскольку x статичен, он должен быть одинаковым независимо от экземпляров. Теперь сравните это с static final int x=5;, который является статической константой времени компиляции и не может быть изменен и, следовательно, остается неизменным. 02.10.2018
  • Следовательно, это потому, что внутреннее всегда (реализацией Java) связано с экземпляром включающего класса; так что этот класс уместен (то есть член) этого последнего, и как нестатический член включающего класса, для каждого его экземпляра внутренний и все его члены выделяются в памяти экземпляра, становясь зависимыми от Сама жизнь экземпляра 05.09.2019

  • 3

    InnerClass не может иметь static участников, потому что он принадлежит экземпляру (из OuterClass). Если вы объявите InnerClass как static, чтобы отделить его от экземпляра, ваш код будет скомпилирован.

    class OuterClass {
        static class InnerClass {
            static int i = 100; // no compile error
            static void f() { } // no compile error
        }
    }
    

    Кстати: вы по-прежнему сможете создавать экземпляры InnerClass. static в этом контексте позволяет этому происходить без включающего экземпляра OuterClass.

    23.12.2009
  • InnerClass не принадлежит OuterClass, экземпляры принадлежат. Сами по себе два класса не имеют такой связи. Вопрос, почему у вас не может быть статических методов в InnerClass, все еще остается в силе. 23.12.2009

  • 4

    Фактически, вы можете объявлять статические поля, если они являются константами и записываются во время компиляции.

    class OuterClass {
        void foo() {
            class Inner{
                static final int a = 5; // fine
                static final String s = "hello"; // fine
                static final Object o = new Object(); // compile error, because cannot be written during compilation
            }
        }
    }
    
    22.12.2014

    5
    1. Последовательность инициализации класса является важной причиной.

    Поскольку внутренние классы зависят от экземпляра включающего класса / Outer, поэтому внешний класс необходимо инициализировать перед инициализацией внутреннего класса.
    Это JLS говорит об инициализации класса. Нам нужно, чтобы класс T был инициализирован, если

    • Используется статическое поле, объявленное T, и поле не является постоянной переменной.

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

    1. Это нарушит некоторые основные правила. вы можете перейти к последнему разделу (до two cases), чтобы избежать нубов

    Одна вещь о static nested class, когда nested class равно static, он будет вести себя как обычный класс во всех отношениях и связан с внешним классом.

    Но концепция Inner class / non-static nested class в том, что он будет связан с instance внешнего / включающего класса. Обратите внимание, что связано с экземпляром, а не с классом. Теперь связывание с экземпляром явно означает, что (из концепции переменной экземпляра) он будет существовать внутри экземпляра и будет отличаться для разных экземпляров.

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

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

    • Если он используется совместно со всем экземпляром внутреннего класса, это нарушит концепцию context of instance (переменная экземпляра). Тогда это НЕТ.
    • Если он не является общим для всех экземпляров, это нарушит концепцию статичности. Снова НЕТ.
    09.09.2016

    6

    Начиная с версии Java 16, это уже не так. Цитата из JEP 395 (о завершении записей):

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

    Действительно, следующий код можно скомпилировать с Java 16 (пробовал с 16.ea.27):

    public class NestingClasses {
    
        public class NestedClass {
    
            static final String CONSTANT = new String(
                    "DOES NOT COMPILE WITH JAVA <16");
    
            static String constant() {
                return CONSTANT;
            }
    
        }
    
    }
    
    04.12.2020
  • Дополнительную информацию можно найти в JDK-8254321. 30.03.2021

  • 7

    Вот мотивация, которую я считаю наиболее подходящей для этого «ограничения»: вы можете реализовать поведение статического поля внутреннего класса как поле экземпляра внешнего объекта; Таким образом, вам не нужны статические поля / методы. Я имею в виду, что все экземпляры внутреннего класса некоторого объекта совместно используют поле (или метод).

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

    public class Outer{
        int nofInner; //this will count the inner class 
                      //instances of this (Outer)object
                      //(you know, they "belong" to an object)
        static int totalNofInner; //this will count all 
                                  //inner class instances of all Outer objects
        class Inner {
            public Inner(){
                nofInner++;
                totalNofInner++;
            }
        }
    }
    
    24.03.2015
  • Но вопрос в том, почему тогда разрешены статические поля, когда они объявлены final? 09.06.2016
  • если вы посмотрите на [stackoverflow.com/a/1954119/1532220] (ответ OscarRyzs выше): его мотивация заключается в том, что значение не может быть связано с переменной. Конечно, если переменная окончательная, вы можете довольно легко узнать, какое значение присвоить (вы должны знать). 10.06.2016

  • 8

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

    ПРИМЕЧАНИЕ: всегда относитесь к внутренним классам как к переменной для внешнего класса, они могут быть статическими или нестатическими, как и любые другие переменные.

    15.12.2017
  • Но внутренний класс может иметь static final константу. 13.04.2019
  • 'Нестатический внутренний' это тавтология. 19.06.2019

  • 9

    Потому что это вызовет двусмысленность в значении слова «статика».

    Внутренние классы не могут объявлять статические члены, кроме констант времени компиляции. Значение слова «статика» было бы двусмысленным. Означает ли это, что на виртуальной машине только один экземпляр? Или только один экземпляр на внешний объект? Разработчики языка решили не заниматься этим вопросом.

    Взято из "Core Java SE 9 для нетерпеливых" Кея С. Хорстманна. Стр. 90 Глава 2.6.3

    05.05.2020

    10

    Думаю, это для последовательности. Хотя для него нет никаких технических ограничений, вы не сможете получить доступ к статическим членам внутреннего класса извне, то есть OuterClass.InnerClass.i, потому что средний шаг не статичен.

    22.06.2016
  • Но внутренний класс может иметь static final константу. 13.04.2019
  • Новые материалы

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

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

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]