Чому ми не можемо мати статичний метод у нестатичному внутрішньому класі?
Якщо я стану внутрішній клас статичним, він працює. Чому?
static
.")
Чому ми не можемо мати статичний метод у нестатичному внутрішньому класі?
Якщо я стану внутрішній клас статичним, він працює. Чому?
static
.")
Відповіді:
Оскільки екземпляр внутрішнього класу неявно пов'язаний з екземпляром його зовнішнього класу, він не може сам визначити будь-які статичні методи. Оскільки статичний вкладений клас не може безпосередньо посилатися на змінні екземпляри або методи, визначені в його огороджувальному класі, він може використовувати їх лише через посилання на об'єкт, статичним методом можна оголосити статичний вкладений клас.
A.B.sync(X)
або навіть (зсередини A) B.sync(x)
?
Немає великого сенсу дозволити статичний метод у нестатичному внутрішньому класі; як би ви отримали доступ до нього? Ви не можете отримати доступ (принаймні спочатку) до нестатичного екземпляра внутрішнього класу, не пройшовши зовнішній екземпляр класу. Суто статичного способу створення нестатичного внутрішнього класу немає.
Для зовнішнього класу Outer
ви можете отримати доступ до статичного методу, test()
подібного до цього:
Outer.test();
Для статичного внутрішнього класу Inner
ви можете отримати доступ до його статичного методу innerTest()
так:
Outer.Inner.innerTest();
Однак, якщо Inner
він не є статичним, тепер немає чисто статичного способу посилання на метод innertest
. Нестатичні внутрішні класи прив'язані до конкретного екземпляра їх зовнішнього класу. Функція відрізняється від постійної тим, що Outer.Inner.CONSTANT
гарантоване посилання на однозначне таким чином, що виклик функції Outer.Inner.staticFunction();
не є. Скажімо, у вас є Inner.staticFunction()
ті дзвінки getState()
, які визначені в Outer
. Якщо ви спробуєте викликати цю статичну функцію, тепер у вас є неоднозначне посилання на клас Внутрішній. Тобто, на якому екземплярі внутрішнього класу ви викликаєте статичну функцію? Це важливо. Дивіться, немає по-справжньому статичного способу посилання на цей статичний метод, через неявне посилання на зовнішній об'єкт.
Пол Беллора правильний, що мовні дизайнери могли це дозволити. Потім їм слід обережно заборонити будь-який доступ до неявного посилання на зовнішній клас у статичних методах нестатичного внутрішнього класу. На сьогоднішній день, яке значення має внутрішній клас, якщо ви не можете посилатися на зовнішній клас, окрім статичного? А якщо статичний доступ нормальний, то чому б не оголосити весь внутрішній клас статичним? Якщо ви просто зробите внутрішній клас статичним, то ви не маєте явних посилань на зовнішній клас, і у вас більше немає цієї неоднозначності.
Якщо вам потрібні статичні методи нестатичного внутрішнього класу, то вам, ймовірно, потрібно переосмислити свій дизайн.
Outer.Inner i = new Outer().new Inner();
Також внутрішні класи можуть оголошувати статичні константи відповідно до пункту 15.28 JLS.
Outer.Inner.staticMethod()
само, як можете отримати доступ Outer.Inner.CONSTANT
. "Ви не можете отримати доступ до ... нестатичного екземпляра внутрішнього класу, не пройшовши зовнішній екземпляр класу." Навіщо тобі потрібен екземпляр? Вам не потрібен примірник Outer
дзвінка Outer.staticMethod()
. Я знаю, що це нерозумно, але я можу сказати, що не має сенсу формувати свою відповідь таким чином. ІМХО мовні дизайнери могли дозволити це, якби побажали.
Outer.Inner.CONSTANT
і Outer.Inner.staticMethod()
полягає в тому, що посилання на константу не має шансу неявно посилатися на той екземпляр, Outer
в якому Inner
був створений екземпляр. Всі посилання Outer.staticMethod()
поділяють той самий точний стан. Всі посилання Outer.Inner.CONSTANT
поділяють той самий точний стан. Однак посилання на Outer.Inner.staticMethod()
неоднозначні: "статичний" стан не є справді статичним, через неявне посилання на зовнішній клас у кожному екземплярі Inner
. Не існує справді однозначного статичного способу доступу до нього.
Outer.this
. Я погоджуюся з дизайнерами мови Java, що немає ніяких підстав дозволяти статичні методи або незакінчені статичні поля у внутрішніх класах, тому що все у внутрішньому класі має бути в контексті класу, що додається.
У мене є теорія, яка може бути, а може і не бути правильною.
По-перше, ви повинні знати деякі речі про те, як внутрішні класи реалізуються в 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
не є статичним. Цей вид пояснює, чому статичні методи заборонені (хоча ви можете зробити аргумент, що ви можете дозволити статичні методи, якщо вони не посилаються на об'єкт, що обгороджує), і чому ви не можете мати неповні статичні поля ( було б контрінтуїтивно, якщо екземпляри нестатичних внутрішніх класів з різних об'єктів поділяли "статичний стан"). Це також пояснює , чому кінцеві поля є дозволено ( до тих пір , поки вони не посилатися на об'єкті захищає).
public static double sinDeg(double theta) { ... }
внутрішній клас математичних утиліт?
Єдина причина - "не обов'язково", то чому б це намагатися підтримувати?
Синтаксично немає причин забороняти внутрішньому класу мати статичні члени. Незважаючи на те, що екземпляр Inner
асоційований з екземпляром Outer
, все ж можна використовувати Outer.Inner.myStatic
для посилання статичного члена, Inner
якщо java вирішить це зробити.
Якщо вам потрібно поділитися чимось серед усіх примірників Inner
, ви можете просто розмістити їх Outer
як статичні члени. Це не гірше, ніж ви використовуєте статичні члени, в Inner
яких Outer
все одно можете отримати доступ до будь-якого приватного члена Inner
(не покращує інкапсуляцію).
Якщо вам потрібно поділитися чимось між усіма екземплярами, Inner
створеними одним outer
об’єктом, має сенс розмістити їх у Outer
класі як звичайних членів.
Я не згоден з думкою, що "статичний вкладений клас - це майже просто клас вищого рівня". Я думаю, що краще ставитись до статичного вкладеного класу / внутрішнього класу як до складу зовнішнього класу, оскільки вони можуть отримати доступ до приватних членів зовнішнього класу. А члени зовнішнього класу також є "членами внутрішнього класу". Тому немає необхідності підтримувати статичний член у внутрішньому класі. Достатній звичайний / статичний член у зовнішньому класі.
З: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Як і у випадку методів і змінних екземплярів, внутрішній клас асоціюється з екземпляром класу, що закривається, і має прямий доступ до методів та полів цього об'єкта. Крім того, оскільки внутрішній клас асоціюється з екземпляром, він не може визначити будь-яких статичних членів.
Пояснення Oracle є поверхневим і ручним. Оскільки немає жодних технічних чи синтаксичних причин для викупу статичних членів у внутрішньому класі (це дозволено іншими мовами, такими як C #), мотивація дизайнерів Java, ймовірно, була концептуальною смаком та / або справою технічної зручності.
Ось моя міркування:
На відміну від класів вищого рівня, внутрішні класи залежать від екземплярів: екземпляр внутрішнього класу асоціюється з екземпляром кожного з його зовнішніх класів і має прямий доступ до своїх членів. Це головна мотивація їх наявності на Java. Висловлено інакше: внутрішній клас призначений для інстанції в контексті зовнішнього екземпляра класу. Без зовнішнього екземпляра класу внутрішній клас не повинен бути більш корисним, ніж інші члени екземпляра зовнішнього класу. Давайте розглянемо це як інстанційно-залежний дух внутрішніх класів.
Сама природа статичних членів (які НЕ об'єктно-орієнтовані) стикається з залежним від екземпляра духом внутрішніх класів (який об'єктно-орієнтований на ІС), оскільки ви можете посилатись / викликати статичний член внутрішнього класу без екземпляра зовнішнього класу шляхом використовуючи кваліфіковану назву внутрішнього класу.
Статичні змінні, зокрема, можуть ображати ще одним способом: два екземпляри внутрішнього класу, пов'язані з різними екземплярами зовнішнього класу, поділять статичні змінні. Оскільки змінні є компонентом стану, два екземпляри внутрішнього класу фактично поділять стан незалежно від екземплярів зовнішнього класу, з якими вони пов'язані. Це не так, що неприпустимо, що статичні змінні працюють таким чином (ми сприймаємо їх у Java як розумний компроміс із чистотою OOP), але, мабуть, є більш глибоке правопорушення, допускаючи їх у внутрішні класи, екземпляри яких уже поєднані з екземплярами зовнішнього класу за дизайном. Заборона статичних членів у внутрішніх класах на користь духу, залежного від екземпляра, пожинає додатковий бонус за виправдання цього більш глибокого правопорушення OOP.
З іншого боку, жодне подібне правопорушення не спричиняється статичними константами, які не мають значення змісту держави, тому вони є допустимими. Чому б не заборонити статичним константам для максимальної узгодженості із залежним від екземпляра духом ? Можливо, тому константи не повинні займати більше пам’яті, ніж потрібно (якщо вони змушені бути нестатичними, вони копіюються у кожен екземпляр внутрішнього класу, який є потенційно марним). Інакше я не можу уявити причину для виключення.
Це може бути не настільки обґрунтованим міркуванням, але ІМО має найбільше сенс короткого зауваження Oracle щодо цього питання.
Коротка відповідь: ментальна модель більшості програмістів має те, як працює область, а не модель, яку використовує 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 повинен буде або розділити діапазони статичних та екземплярів, і підтримувати ієрархії розгалуження та приєднання, або йому доведеться розширити просту булеву ідею "статичного контексту", щоб змінити, щоб відстежувати тип контексту на всіх рівнях вкладеного класу в поточній області.
Чому ми не можемо мати статичний метод у нестатичному внутрішньому класі?
Примітка: нестатичний вкладений клас відомий як внутрішній клас, тому у вас його немаєnon-static inner class
.
Екземпляр внутрішнього класу не існує без відповідного екземпляра зовнішнього класу. Внутрішній клас не може оголошувати статичні члени, крім констант часу часу. Якби це було дозволено, тоді було б неоднозначне значення сенсу static
. У цьому випадку були б певні плутанини:
Ось чому дизайнери, ймовірно, прийняли рішення взагалі не займатися цим питанням.
Якщо я стану внутрішній клас статичним, він працює. Чому?
Знову не можна зробити внутрішній клас статичним, а можна оголосити статичний клас вкладеним. У цьому випадку цей вкладений клас насправді є частиною зовнішнього класу і може мати статичні члени без будь-якої проблеми.
Ця тема привернула увагу багатьох, все ж я спробую пояснити найпростішими термінами.
По-перше, з посиланням на http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , клас або інтерфейс ініціалізується безпосередньо перед першим виникненням / викликом будь-якого учасника, перед яким перебуває статичне ключове слово.
Отже, якщо ми поставимо статичний член у внутрішньому класі, це призведе до ініціалізації внутрішнього класу, не обов'язково зовнішнього / огороджуючого класу. Отже, ми перешкоджаємо послідовності ініціалізації класу.
Також врахуйте той факт, що нестатичний внутрішній клас асоціюється з екземпляром класу, що закривається / зовнішнього. Таким чином, асоціація з екземпляром означатиме, що внутрішній клас буде існувати всередині екземпляра Зовнішнього класу і відрізнятиметься між примірниками.
Спрощуючи крапку, для доступу до статичного члена нам потрібен екземпляр Зовнішнього класу, з якого нам знову потрібно буде створити екземпляр нестатичного внутрішнього класу. Статичні члени не повинні прив'язуватися до примірників, тому ви отримуєте помилку компіляції.
Внутрішній клас - це щось зовсім інше від статичного вкладеного класу, хоча обидва схожі за синтаксисом. Статичні вкладені класи є лише засобом для групування, тоді як внутрішні класи мають сильну асоціацію - і доступ до всіх значень - їх зовнішнього класу. Ви повинні бути впевнені, чому ви хочете використовувати внутрішній клас, і тоді це повинно бути цілком природним, який саме ви повинні використовувати. Якщо вам потрібно оголосити статичний метод, це, мабуть, статичний вкладений клас, який ви все одно хочете.
припустимо, є два екземпляри зовнішнього класу, і вони обидва мають внутрішній внутрішній клас. Тепер, якщо внутрішній клас має один статичний член, він зберігатиме лише одну копію цього члена в купі. У цьому випадку обидва об'єкти зовнішнього класу будуть посилатися на це одна копія, і вони можуть змінити її разом. Це може спричинити ситуацію "Брудне читання", отже, запобігти застосуванню цього Java цього обмеження. Ще одним вагомим моментом в підтримці цього аргументу є те, що java допускає тут остаточних статичних членів, тих, значення яких не можуть бути змінено з будь-якого об'єкта зовнішнього класу. Будь ласка, дозвольте мені, якщо я помиляюся.
Перш за все, чому хтось хоче визначити статичний член у нестатичному внутрішньому класі? відповідь полягає в тому, щоб зовнішній член класу міг використовувати цей статичний член лише з ім'ям внутрішнього класу, правда?
Але для цього випадку ми можемо безпосередньо визначити члена у зовнішньому класі. який буде пов'язаний з усіма об'єктами внутрішнього класу, в екземплярі зовнішнього класу.
як код нижче,
public class Outer {
class Inner {
public static void method() {
}
}
}
можна написати так
public class Outer {
void method() {
}
class Inner {
}
}
Отже, на мою думку, не ускладнювати код дизайнер Java не дозволяє цю функціональність, або ми можемо побачити цю функціональність у майбутніх випусках із деякими іншими функціями.
Спробуйте ставитися до класу як до звичайного поля, тоді ви зрозумієте.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Марно мати членів внутрішнього класу як статичних, оскільки ви не зможете отримати доступ до них в першу чергу.
Подумайте про це, щоб отримати доступ до статичного члена, який ви використовуєте className.memberName,, у нашому випадку це має бути щось на зразок externalclassName.innerclassName.memberName ,,, тепер ви бачите, чому внутрішній клас повинен бути статичним ....