Чому ми не можемо мати статичний метод у (нестатичному) внутрішньому класі?


142

Чому ми не можемо мати статичний метод у нестатичному внутрішньому класі?

Якщо я стану внутрішній клас статичним, він працює. Чому?


4
Тому що тепер Java є те , що старий COBOL :)
SES

Суть полягає в тому, що вони ще не реалізували її.
intrepidis

1
"Нестатичний внутрішній" - це тавтологія.
Маркіз Лорн

Якщо ви не хочете піддавати свій внутрішній клас іншим і сподіваєтесь, що він містить статичні методи, ви можете помістити модифікатори як "приватні", так і "статичні" на внутрішній клас.
Віллі Z

Внутрішній клас за визначенням не є статичним. Ви можете мати статичний вкладений / член-клас та нестатичний вкладений / член-клас. Останній також відомий як внутрішній клас. (Довідка: розділ 8.1.3 JLS зазначає " Внутрішній клас - це вкладений клас, який явно або неявно не декларується static.")
Ервін Болвідт,

Відповіді:


111

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


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

15
В C ++ ви можете мати, так що це помилка на мові Java.
Промислово-антидепресант

39
Слово помилка ... Я не думаю, що це слово означає те, що ви думаєте, що воно означає.
Сет Нельсон

24
Більш підходящою фразою було б "дратувати як матір". Не розумію, чому Java цього не дозволяє. Іноді я хочу, щоб внутрішній клас використовував властивості батьківського класу, але зберігати статичні методи для кращого простору імен. Чи є щось по суті з цим не так? :(
Angad

6
Саме так. Я хочу написати внутрішній клас корисної програми. Деякі з його методів отримали б користь від доступу до зовнішнього класу, тому я не можу зробити його статичним, але деякі його методи - це лише функції утиліти. Чому я не можу дзвонити A.B.sync(X)або навіть (зсередини A) 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. Якщо ви спробуєте викликати цю статичну функцію, тепер у вас є неоднозначне посилання на клас Внутрішній. Тобто, на якому екземплярі внутрішнього класу ви викликаєте статичну функцію? Це важливо. Дивіться, немає по-справжньому статичного способу посилання на цей статичний метод, через неявне посилання на зовнішній об'єкт.

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

Якщо вам потрібні статичні методи нестатичного внутрішнього класу, то вам, ймовірно, потрібно переосмислити свій дизайн.


6
-1 Я не погоджуюся з кутом, який ти тут взяв. Безумовно, ми можемо віднести внутрішній тип класу, наприклад. Outer.Inner i = new Outer().new Inner();Також внутрішні класи можуть оголошувати статичні константи відповідно до пункту 15.28 JLS.
Пол Беллора

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не є статичним. Цей вид пояснює, чому статичні методи заборонені (хоча ви можете зробити аргумент, що ви можете дозволити статичні методи, якщо вони не посилаються на об'єкт, що обгороджує), і чому ви не можете мати неповні статичні поля ( було б контрінтуїтивно, якщо екземпляри нестатичних внутрішніх класів з різних об'єктів поділяли "статичний стан"). Це також пояснює , чому кінцеві поля є дозволено ( до тих пір , поки вони не посилатися на об'єкті захищає).


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

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

Пояснення Oracle є поверхневим і ручним. Оскільки немає жодних технічних чи синтаксичних причин для викупу статичних членів у внутрішньому класі (це дозволено іншими мовами, такими як C #), мотивація дизайнерів Java, ймовірно, була концептуальною смаком та / або справою технічної зручності.

Ось моя міркування:

На відміну від класів вищого рівня, внутрішні класи залежать від екземплярів: екземпляр внутрішнього класу асоціюється з екземпляром кожного з його зовнішніх класів і має прямий доступ до своїх членів. Це головна мотивація їх наявності на Java. Висловлено інакше: внутрішній клас призначений для інстанції в контексті зовнішнього екземпляра класу. Без зовнішнього екземпляра класу внутрішній клас не повинен бути більш корисним, ніж інші члени екземпляра зовнішнього класу. Давайте розглянемо це як інстанційно-залежний дух внутрішніх класів.

Сама природа статичних членів (які НЕ об'єктно-орієнтовані) стикається з залежним від екземпляра духом внутрішніх класів (який об'єктно-орієнтований на ІС), оскільки ви можете посилатись / викликати статичний член внутрішнього класу без екземпляра зовнішнього класу шляхом використовуючи кваліфіковану назву внутрішнього класу.

Статичні змінні, зокрема, можуть ображати ще одним способом: два екземпляри внутрішнього класу, пов'язані з різними екземплярами зовнішнього класу, поділять статичні змінні. Оскільки змінні є компонентом стану, два екземпляри внутрішнього класу фактично поділять стан незалежно від екземплярів зовнішнього класу, з якими вони пов'язані. Це не так, що неприпустимо, що статичні змінні працюють таким чином (ми сприймаємо їх у Java як розумний компроміс із чистотою OOP), але, мабуть, є більш глибоке правопорушення, допускаючи їх у внутрішні класи, екземпляри яких уже поєднані з екземплярами зовнішнього класу за дизайном. Заборона статичних членів у внутрішніх класах на користь духу, залежного від екземпляра, пожинає додатковий бонус за виправдання цього більш глибокого правопорушення OOP.

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

Це може бути не настільки обґрунтованим міркуванням, але ІМО має найбільше сенс короткого зауваження 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 static -> Outer instance -> instanceMethod()
         \----> staticMethod()

У такому способі мислення про це, звичайно, staticMethod () можуть бачити лише статичні члени Зовнішніх. Але якщо так працює javac, то посилання на змінну екземпляра в статичному методі призведе до помилки "ім'я не може бути вирішено". Що насправді відбувається, так це те, що ім'я знайдено в області застосування, але потім з'являється додатковий рівень перевірки і з'ясовується, що ім'я було оголошено в контексті примірника і на нього посилається статичний контекст.

Добре, як це стосується внутрішніх класів? Наївно, ми вважаємо, що немає причин, що внутрішні класи не можуть мати статичну область, тому що ми зображуємо область, що працює так:

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

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

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

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


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

3

Чому ми не можемо мати статичний метод у нестатичному внутрішньому класі?

Примітка: нестатичний вкладений клас відомий як внутрішній клас, тому у вас його немаєnon-static inner class .

Екземпляр внутрішнього класу не існує без відповідного екземпляра зовнішнього класу. Внутрішній клас не може оголошувати статичні члени, крім констант часу часу. Якби це було дозволено, тоді було б неоднозначне значення сенсу static. У цьому випадку були б певні плутанини:

  1. Чи означає це, що в VM є лише один екземпляр?
  2. Або лише один екземпляр на зовнішній об’єкт?

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

Якщо я стану внутрішній клас статичним, він працює. Чому?

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


3

Ця тема привернула увагу багатьох, все ж я спробую пояснити найпростішими термінами.

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

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

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

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


2

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


Бенедикт, що ти маєш на увазі, коли ти кажеш, що "статичні вкладені класи - це лише засіб для групування"?
Анкур

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 

-1

Марно мати членів внутрішнього класу як статичних, оскільки ви не зможете отримати доступ до них в першу чергу.

Подумайте про це, щоб отримати доступ до статичного члена, який ви використовуєте className.memberName,, у нашому випадку це має бути щось на зразок externalclassName.innerclassName.memberName ,,, тепер ви бачите, чому внутрішній клас повинен бути статичним ....


-2

Вам дозволяються статичні методи на статичних вкладених класах. Наприклад

public class Outer {

  public static class Inner {

    public static void method() {

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