Привіт! На сьогоднішньому занятті ми продовжимо розглядати тему вкладених класів. Настала черга останньої групи — анонімних внутрішніх класів у Java.
Давай повернемося до нашої схеми:
Як і локальні класи, про які ми говорили в минулій лекції, анонімні — підвид внутрішніх класів. У них також є декілька схожостей і відмінностей між собою.
Але для початку давай розберемося: чому ж вони, власне, називаються «анонімними»?
Для цього розглянемо простий приклад.
Уяви, що в нас є основна програма, яка постійно працює і щось робить. Ми хочемо створити для цієї програми систему моніторингу з кількох модулів.
Один модуль буде відстежувати загальні показники роботи та вести лог, другий — фіксувати і реєструвати помилки в журналі помилок, третій — відстежувати підозрілу активність: наприклад, спроби несанкціонованого доступу та інші пов'язані з безпекою речі.
Оскільки всі три модулі повинні, по суті, просто стартувати на початку програми і працювати у фоновому режимі, буде гарною ідеєю створити для них спільний інтерфейс:
По суті, ми повинні просто створити 3 об'єкти —
Як і локальні класи, про які ми говорили в минулій лекції, анонімні — підвид внутрішніх класів. У них також є декілька схожостей і відмінностей між собою.
Але для початку давай розберемося: чому ж вони, власне, називаються «анонімними»?
Для цього розглянемо простий приклад.
Уяви, що в нас є основна програма, яка постійно працює і щось робить. Ми хочемо створити для цієї програми систему моніторингу з кількох модулів.
Один модуль буде відстежувати загальні показники роботи та вести лог, другий — фіксувати і реєструвати помилки в журналі помилок, третій — відстежувати підозрілу активність: наприклад, спроби несанкціонованого доступу та інші пов'язані з безпекою речі.
Оскільки всі три модулі повинні, по суті, просто стартувати на початку програми і працювати у фоновому режимі, буде гарною ідеєю створити для них спільний інтерфейс:
public interface MonitoringSystem {
public void startMonitoring();
}
Його будуть імплементувати 3 конкретних класи:
public class GeneralIndicatorsMonitoringModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Моніторинг загальних показників стартував!");
}
}
public class ErrorMonitoringModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Моніторинг відстеження помилок стартував!");
}
}
public class SecurityModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Моніторинг безпеки стартував!");
}
}
Здавалося б, усе гаразд. У нас є доволі зрозуміла система з кількох модулів. У кожного з них є власна поведінка. Якщо нам знадобляться нові модулі, ми зможемо їх додати, адже у нас є інтерфейс, який досить легко імплементувати.
Але давай подумаємо про те, як буде працювати наша система моніторингу.
По суті, ми повинні просто створити 3 об'єкти — GeneralIndicatorsMonitoringModule, ErrorMonitoringModule, SecurityModule — і викликати метод startMonitoring() у кожного з них.
Тобто, все, що потрібно зробити — створити 3 об'єкти і викликати у них 1 метод.
public class Main {
public static void main(String[] args) {
GeneralIndicatorsMonitoringModule generalModule = new GeneralIndicatorsMonitoringModule();
ErrorMonitoringModule errorModule = new ErrorMonitoringModule();
SecurityModule securityModule = new SecurityModule();
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
}
}
Вивід у консоль:
Моніторинг загальних показників стартував!
Моніторинг відстеження помилок стартував!
Моніторинг безпеки стартував!
І для такої невеликої роботи ми написали цілу систему: 3 класи і один інтерфейс! І все це — заради 6 рядків коду.
З іншого боку, які у нас варіанти?
Так, не дуже приємно, що ми написали такі «одноразові» класи. Але як ми можемо це виправити?
Тут нам і приходять на допомогу анонімні внутрішні класи!
Ось як вони виглядають у нашому випадку:
public class Main {
public static void main(String[] args) {
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг загальних показників стартував!");
}
};
MonitoringSystem errorModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг відстеження помилок стартував!");
}
};
MonitoringSystem securityModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг безпеки стартував!");
}
};
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
}
}
Давай розбиратися, що тут відбувається!
Виглядає так, ніби ми створюємо об'єкт інтерфейсу:
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг загальних показників стартував!");
}
};
Але ж ми давно знаємо, що створювати об'єкти інтерфейсів не можна!
Так і є, не можна. Насправді ми цього і не робимо.
У той момент, коли ми пишемо:
MonitoringSystem generalModule = new MonitoringSystem() {
};
всередині Java-машини відбувається наступне:
- Створюється безіменний Java-клас, що реалізує інтерфейс
MonitoringSystem. - Компилятор, побачивши такий клас, вимагає від тебе реалізувати всі методи інтерфейсу
MonitoringSystem(ми це і зробили 3 рази). - Створюється один об'єкт цього класу. Зверни увагу на код:
MonitoringSystem generalModule = new MonitoringSystem() {
};
В кінці стоїть крапка з комою! Вона стоїть там не просто так. Ми одночасно оголошуємо клас (через фігурні дужки) і створюємо його об'єкт за допомогою ();
Кожен із наших трьох об'єктів перевизначив метод startMonitoring() по-своєму.
У кінці ми просто викликаємо цей метод у кожного з них:
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
Вивід у консоль:
Моніторинг загальних показників стартував!
Моніторинг відстеження помилок стартував!
Моніторинг безпеки стартував!
Ось і все! Ми виконали своє завдання: створили три об'єкти MonitoringSystem, перевизначили його трьома різними способами і викликали тричі.
Усі три модулі успішно запущені і працюють.
При цьому структура нашої програми стала значно простішою! Адже класи GeneralIndicatorsMonitoringModule, ErrorMonitoringModule, SecurityModule тепер взагалі можна видалити з програми!
Вони нам просто не потрібні — ми прекрасно впоралися і без них.
Якщо кожному з наших анонімних класів-модулів знадобиться якесь відмінне поведінка, свої специфічні методи, яких немає в інших, ми легко можемо дописати їх:
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг загальних показників стартував!");
}
public void someSpecificMethod() {
System.out.println("Специфічний метод тільки для першого модуля");
}
};
У документації Oracle наведена хороша рекомендація: «Застосовуйте анонімні класи, якщо вам потрібен локальний клас для одноразового використання».
Анонімний клас — це повноцінний внутрішній клас. Тому він має доступ до змінних зовнішнього класу, включаючи статичні і приватні:
public class Main {
private static int currentErrorsCount = 23;
public static void main(String[] args) {
MonitoringSystem errorModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Моніторинг відстеження помилок стартував!");
}
public int getCurrentErrorsCount() {
return currentErrorsCount;
}
};
}
}
Є у них дещо спільне і з локальними класами: вони видно лише всередині того методу, в якому визначені. У прикладі вище, будь-які спроби звернутися до об'єкта errorModule за межами методу main() будуть невдалими.
І ще одне важливе обмеження, яке дісталося анонімним класам від їхніх «предків» — внутрішніх класів: анонімний клас не може містити статичні змінні і методи.
Якщо ми спробуємо зробити метод getCurrentErrorsCount() з прикладу вище статичним, компілятор викине помилку:
//помилка! Inner classes cannot have static declarations
public static int getCurrentErrorsCount() {
return currentErrorsCount;
}
Той самий результат ми отримаємо, якщо спробуємо оголосити статичну змінну:
MonitoringSystem errorModule = new MonitoringSystem() {
//помилка! Inner classes cannot have static declarations!
static int staticInt = 10;
@Override
public void startMonitoring() {
System.out.println("Моніторинг відстеження помилок стартував!");
}
};
Що ж ми вивчатимемо про вкладені класи далі? Скоро ти обов'язково дізнаєшся! :)
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ