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

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

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

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

Ответ 1

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

8.1.2. Внутренние классы и экземпляры Enclosing

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

Ответ 2

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

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

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

Это похоже на то, что вы одновременно пытаетесь создать атрибут static/instance.

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

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

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

Ответ 3

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

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

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

Ответ 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
        }
    }
}

Ответ 5

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

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

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++;
        }
    }
}

Ответ 6

  • Класс Инициализация - критическая причина.

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

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

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

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

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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