Чому Java забороняє статичні поля у внутрішніх класах?


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

Хоча неможливо отримати доступ до статичного поля за допомогою OuterClass.InnerClass.i, якщо я хочу записати щось, що повинно бути статичним, наприклад, кількість створених об'єктів InnerClass, було б корисно зробити це поле статичним. То чому Java забороняє статичні поля / методи у внутрішніх класах?

EDIT: Я знаю, як зробити компілятор задоволеним статичним вкладеним класом (або статичним внутрішнім класом), але те, що я хочу знати, - це те, чому java забороняє статичні поля / методи всередині внутрішніх класів (або звичайний внутрішній клас) як з мовного дизайну, так і з аспекти реалізації, якщо хтось знає про це більше.


3
Мій улюблений приклад - це реєстратор для внутрішнього класу. Він не може бути статичним, як і всі інші реєстратори.
Piotr Findeisen

Відповіді:


32

Ідея внутрішніх класів полягає в тому, щоб діяти в контексті екземпляра, що вкладається. Якось, дозволяючи статичні змінні та методи суперечить цій мотивації?

8.1.2 Внутрішні класи та огороджувальні екземпляри

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


18
може бути , це просто вирішив так
Грегорі Pakosz

3
ви не можете створити екземпляр нестатичного внутрішнього елемента без батьківського посилання, але ви все одно можете його ініціалізувати .
skaffman

Якщо ClassLoaders зберігають кеш із написом "Клас X було ініціалізовано", то їх логіка не може використовуватися для ініціалізації декількох екземплярів Class [об'єкти, що представляють] X (що потрібно, коли об'єкти Class повинні бути створені як внутрішні класи всередині декількох окремі об'єкти).
Erwin Smout,

@skaffman Це все ще не має сенсу. Статичні властивості внутрішнього класу будуть ініціалізовані лише один раз, то в чому проблема? Зараз у мене є статична хеш-карта, і я маю близько 4 методів, які маніпулюють лише цією картою, що робить більш ідеальним групувати все у внутрішньому класі. Однак статична хеш-карта тепер повинна була жити зовні і, можливо, з іншими речами, що просто дурно. У чому б проблема з ініціалізацією статичних властивостей?
mmm

54

я хочу знати, чому 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, йому вже не потрібен живий екземпляр, щоб жити самим собою. Тепер, коли немає залежності, ви можете вільно оголосити статичний атрибут.

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


4
Я придбаю ваше пояснення щодо статичних властивостей внутрішнього класу, але, як @skaffman зазначає у коментарі до моєї відповіді, а як щодо статичних методів ? Здається, ніби методи повинні бути дозволені, не вимагаючи їх від'єднання від будь-якого екземпляра. Дійсно, у Java ви можете викликати статичні методи на екземплярах (хоча це вважається поганим стилем). До речі: я попросив колегу спробувати скомпілювати код OP як C #, і він справді компілюється. Отже, C #, мабуть, дозволяє це, що демонструє, що те, що хоче зробити OP, не порушує якогось фундаментального принципу OO.
Асаф,

2
Те саме відбувається з методами. Справа тут не в атрибутах чи методах, які є statis чи ні, а в тому, що внутрішній клас він сам є екземпляром "речі". Я маю на увазі, проблема тут полягає в тому, що екземпляр такого внутрішнього класу не існує, поки не буде створений зовнішній клас. Отже, який метод буде відправлений тоді, якщо нічого немає. Ви б потрапили в повітря лише тому, що вам спочатку потрібен екземпляр.
OscarRyz

1
Про C # ... ну. Він не є дійсним лише через те, що C # дозволяє це, я не маю на увазі, що це неправильно, але C # включає кілька парадигм, що полегшують розробку навіть ціною послідовності (ви повинні вивчати нові речі з кожним випуском .NET), і це дозволяє це та інші види речей серед інших. Я думаю, що це добре. Якщо спільнота вважає додаткову функцію досить крутою, C # може мати її в майбутньому.
OscarRyz

2
@OscarRyz Навіщо потрібні екземпляри внутрішнього класу, щоб використовувати його статичні методи / поля? І прикладом [корисного] статичного методу у внутрішньому класі є приватний допоміжний метод.
Леонід Семенов

1
З використанням finalстатичних полів дозволено у внутрішньому класі Java. Як ви пояснюєте цей сценарій?
Номер 945

34

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.


6
InnerClassзовсім НЕ належить OuterClass, екземпляри з нього зробити. Самі два класи не мають таких стосунків. Питання про те, чому ви не можете мати статичних методів у InnerClassвсе ще стоїть.
skaffman

9

Насправді, ви можете оголосити статичні поля, якщо вони є константами і записані під час компіляції.

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

8
  1. Послідовність ініціалізації класу є критичною причиною.

Оскільки внутрішні класи залежать від екземпляра класу enclosing / Outer, так і Outer клас потрібно ініціалізувати перед ініціалізацією внутрішнього класу.
Це JLS говорить про ініціалізацію класу. Суть, який нам потрібен, полягає в тому, що клас T буде ініціалізований if

  • Використовується статичне поле, оголошене T, і поле не є постійною змінною.

Отже, якщо внутрішній клас має доступ до статичного поля, що спричинить ініціалізацію внутрішнього класу, але це не забезпечить ініціалізацію закритого класу.

  1. Це порушило б деякі основні правила . Ви можете перейти до останнього розділу (до two cases), щоб уникнути жодної речі

Одна річ про те , коли деякі єstatic nested classnested classstatic він буде вести себе так само , як звичайний клас в усіх відношеннях , і це пов'язано із зовнішнім класом.

Але концепція Inner class/ is це буде пов'язана з класом зовнішнього / огороджувального. Зверніть увагу, пов’язане з екземпляром, а не класом. Тепер асоціювання з екземпляром однозначно означає, що ( з поняття змінної екземпляра ) воно буде існувати всередині екземпляра і буде відрізнятися між екземплярами. non-static nested classinstance

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

Отже, якщо Java дозволяє нам використовувати статичну змінну всередині не статичного вкладеного класу. буде дві справи .

  • Якщо його спільно використовувати з усіма екземплярами внутрішнього класу, це порушить концепцію context of instance(змінної екземпляра). Тоді це НІ.
  • Якщо його не надаватимуть всій інстанції, це порушить концепцію статичності. Знову НІ.

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

2
Але питання в тому, що є причиною дозволу статичних полів, коли вони оголошені finalтоді?
Заспокоєння

якщо ви подивитесь на [ stackoverflow.com/a/1954119/1532220 ] (відповідь OscarRyzs вище): його мотивація полягає в тому, що значення не може бути пов’язане зі змінною. Звичайно, якщо змінна остаточна, ви можете досить легко знати, яке значення призначати (ви повинні знати).
ianos

2

Простими словами, нестатичні внутрішні класи є змінними екземпляра для зовнішнього класу, і вони створюються лише тоді, коли створюється зовнішній клас і зовнішній об'єкт класу створюється під час виконання, тоді як статичні змінні створюються під час завантаження класу. Отже, нестатичний внутрішній клас - це середовище виконання, тому статичний не є частиною нестатичного внутрішнього класу.

ПРИМІТКА: обробляйте внутрішні класи завжди як змінні для зовнішнього класу, вони можуть бути статичними або нестатичними, як і будь-які інші змінні.


Але внутрішній клас може мати static finalконстанти.
Дощ


1

Тому що це спричиняло б двозначність у значенні «статичний».

Внутрішні класи не можуть оголошувати статичні члени, крім констант часу компіляції. Існувала б неоднозначність щодо значення поняття "статичний". Чи означає це, що у віртуальній машині є лише один екземпляр? Або лише один екземпляр на зовнішній об’єкт? Мовні дизайнери вирішили не вирішувати цю проблему.

Взято з "Core Java SE 9 для нетерплячих" Кей С. Хорстманн. Сторінка 90 Розділ 2.6.3


-1

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


Але внутрішній клас може мати static finalконстанти.
Дощ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.