Почему Java запрещает статические поля во внутренних классах? - PullRequest
78 голосов
/ 23 декабря 2009
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

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

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

Ответы [ 8 ]

43 голосов
/ 23 декабря 2009

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

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

33 голосов
/ 23 декабря 2009

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.

26 голосов
/ 23 декабря 2009

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

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

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

8 голосов
/ 22 декабря 2014

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

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 голосов
/ 09 сентября 2016
  1. класс Инициализация последовательности является критической причиной.

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

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

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

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

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

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

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

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

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

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

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

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++;
        }
    }
}
2 голосов
/ 15 декабря 2017

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

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

0 голосов
/ 22 июня 2016

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

...