тл; д-р
Для полів , int b = b + 1
є незаконним , оскільки b
нелегальної вперед посилання b
. Ви насправді можете це виправити, написавши int b = this.b + 1
, що складається без скарг.
Для локальних змінних , int d = d + 1
є незаконним , оскільки d
НЕ инициализируется перед використанням. Це не стосується полів, які завжди ініціалізуються за замовчуванням.
Різницю можна побачити, намагаючись компілювати
int x = (x = 1) + x;
як польова декларація та як декларація локальної змінної. Перший зазнає невдачі, але другий досягне успіху через різницю в семантиці.
Вступ
По-перше, правила для ініціалізаторів полів та локальних змінних дуже різні. Тож ця відповідь стосуватиметься правил у двох частинах.
Ми будемо використовувати цю програму тестування протягом усього:
public class test {
int a = a = 1;
int b = b + 1;
public static void Main(String[] args) {
int c = c = 1;
int d = d + 1;
}
}
Визначення b
недійсним і не вдається з illegal forward reference
помилкою.
Визначення d
недійсним і не вдається з variable d might not have been initialized
помилкою.
Той факт, що ці помилки різні, повинен натякати на те, що причини помилок також різні.
Поля
Польові ініціалізатори на Java керуються JLS § 8.3.2 , Ініціалізація полів.
Сфера поля визначається в JLS §6.3 , Область декларації.
Відповідні правила:
- Обсяг декларації члена,
m
оголошеного в або успадкованого класом типу C (§ 8.1.6), є цілим текстом С, включаючи будь-які вкладені декларації типу.
- Вирази ініціалізації для змінних екземпляра можуть використовувати просте ім'я будь-якої статичної змінної, оголошеної або успадкованої класом, навіть такої, оголошення якої відбувається в текстовому порядку пізніше.
- Використання змінних екземпляра, декларації яких з’являються після тексту, іноді обмежуються, навіть якщо ці змінні екземпляра знаходяться в області дії. Точні правила, що регулюють пряме посилання на змінні екземплярів, див. У § 8.3.2.3.
Пункт 8.3.2.3 говорить:
Декларація члена повинна з'являтися текстово, перш ніж вона буде використана, лише якщо член є екземплярним (відповідно статичним) полем класу або інтерфейсу C і виконуються всі наступні умови:
- Використання відбувається в екземплярі (відповідно статичному) змінному ініціалізаторі C або в екземплярі (відповідно статичному) ініціалізаторі C.
- Використання не з лівого боку завдання.
- Використання відбувається за допомогою простої назви.
- C - це найглибший клас або інтерфейс, що додає до використання.
Насправді ви можете посилатися на поля до того, як вони були оголошені, за винятком певних випадків. Ці обмеження призначені для запобігання коду
int j = i;
int i = j;
від складання. Спеціалізація Java говорить, що "обмеження, наведені вище, призначені для того, щоб під час компіляції зафіксувати кругові чи неправильні ініціалізації.
До чого насправді зводяться ці правила?
Коротше кажучи, правила в основному кажуть, що ви повинні оголосити поле до посилання на це поле, якщо (a) посилання знаходиться в ініціалізаторі, (b) посилання не призначено, (c) посилання є просте ім'я (немає таких класифікаторів this.
) та (г) до нього не можна отримати доступ з внутрішнього класу. Отже, пряма посилання, яка задовольняє всі чотири умови, є незаконною, але пряма посилання, яка не відповідає принаймні одній умові, є ОК.
int a = a = 1;
компілюється , тому що він порушує (б): посилання a
буде бути призначена, так що це законно , щоб звернутися до a
заздалегідь a
«s повної декларації.
int b = this.b + 1
також компілюється, оскільки порушує (c): посилання this.b
- це не проста назва (воно кваліфікується this.
). Ця непарна конструкція все ще є чітко визначеною, оскільки this.b
має значення нуль.
Отже, в основному обмеження щодо посилань на поле всередині ініціалізаторів не дозволяють int a = a + 1
успішно складатись.
Зауважте, що декларацію поля int b = (b = 1) + b
не вдасться скомпілювати, оскільки остаточне b
все ще є незаконним прямим посиланням.
Локальні змінні
Оголошення локальних змінних регулюються JLS §14.4 , Заяви про декларацію місцевих змінних.
Область дії локальної змінної визначена в JLS §6.3 , Сфера декларації:
- Область застосування локальної змінної в блоці (§14.4) - це решта блоку, в якому з’являється декларація, починаючи з власного ініціалізатора і включаючи будь-які подальші декларатори праворуч у операторі декларації локальної змінної.
Зверніть увагу, що ініціалізатори знаходяться в межах декларованої змінної. То чому не int d = d + 1;
компілює?
Причина пов’язана з правилом Java про певне призначення ( JLS §16 ). Визначене призначення в основному говорить про те, що кожен доступ до локальної змінної повинен мати попереднє призначення до цієї змінної, а компілятор Java перевіряє цикли та гілки, щоб переконатися, що призначення завжди відбувається перед будь-яким використанням (саме тому певне призначення має цілий розділ специфікації, присвячений до нього). Основне правило:
- Для кожного доступу до локальної змінної або порожній кінцевого поля
x
, x
повинні бути безумовно присвоєної перед в'їздом, або відбувається помилка часу компіляції.
Вхід int d = d + 1;
доступ до d
локальної змінної вирішений штрафом, але оскільки до d
нього не було призначено d
, компілятор видає помилку. У int c = c = 1
, c = 1
буває, спочатку, що призначає c
, а потім c
ініціалізується на результат цього призначення (що дорівнює 1).
Зауважте, що через певні правила присвоєння, локальне оголошення змінної int d = (d = 1) + d;
буде складено успішно ( на відміну від декларації поля int b = (b = 1) + b
), оскільки d
воно, безумовно, призначається часом досягнення остаточного d
значення.
static
до змінної class-range, як і вstatic int x = x + 1;
, ви отримаєте ту ж помилку? Тому що в C # це має значення, якщо воно статичне або нестатичне.