— Привіт, Аміго! Давай розберемо нову тему – анонімні вкладені класи.
Іноді в Java трапляється ситуація, коли потрібно успадкувати клас від декількох класів. Оскільки множинне успадкування класів у Java заборонено, цю проблему вирішують за допомогою внутрішніх класів: у потрібному класі ми оголошуємо внутрішній клас і успадковуємо його від необхідного класу. Приклад:
class Tiger extends Cat
{
public void tigerRun()
{
.....
}
public void startTiger()
{
TigerThread thread = new TigerThread();
thread.start();
}
class TigerThread extends Thread
{
public void run()
{
tigerRun();
}
}
}
Давай розглянемо цей приклад:
Нам потрібен клас, успадкований від Thread, щоб перевизначити у нього метод run.
Для цього всередині класу Tiger ми оголосили внутрішній клас TigerThread, який успадкували від Thread і в якого перевизначили метод run.
Для нашої зручності у класі Tiger ми оголосили два методи (аналоги методів run та start класу Thread) – tigerRun та startTiger.
У методі startTiger ми створюємо об'єкт типу TigerThread і викликаємо у нього метод start().
При цьому Java-машина створить новий потік, і цей потік почне роботу з виклику методу run, класу TigerThread.
А цей метод, у свою чергу, викличе наш метод run – метод tigerRun.
— З потоками я вже мав справу, ніби не складно.
А обов'язково називати методи tigerRun і startTiger?
— Ні, можна було назвати run і start, але я хотів додатково показати, що ми не успадковуємося від Thread. До того ж, ти міг сильніше заплутатися у моєму поясненні.
— Ок. Тоді все зрозуміло. Хоча під час виклику методу startTiger вдруге ми створимо ще один клас Thread і запустимо його. Чи не вийде, що у нас «один тигр бігатиме у двох різних потоках»?
— Який ти уважний. Згодна, це недобре. Давай перепишемо код таким чином:
class Tiger extends Cat
{
public void tigerRun()
{
.....
}
public void startTiger()
{
thread.start();
}
private TigerThread thread = new TigerThread();
private class TigerThread extends Thread
{
public void run()
{
tigerRun();
}
}
}
— Не те щоб чудово. Двічі однаково викликати такий метод не можна. Але цього разу ми хоча б не створюватимемо другий потік і вдаватимемо, що все добре.
— Правильно, запустив вдруге тигра – отримай Exception.
— Я вже краще за тебе бачу помилки!
Ти молодець! Тоді перейдемо до анонімних внутрішних класів.
Зверни увагу на кілька аспектів вищезазначеного коду:
1) Ми успадкувалися від класу Thread, але фактично не дописали туди жодного коду. Нам довелося успадкуватись, а не «ми успадкували з метою розширити клас Thread».
2) Буде створений лише один об'єкт класу TigerThread.
Тобто щоб перевизначити один метод і створити один об'єкт, ми написали цілу купу коду.
Пам'ятаєш, як я розповідала про появу конструкторів?
До винайдення | Після винайдення |
---|---|
|
|
— Бачу, що код став компактнішим, але не зовсім розумію, що сталося.
— Ми можемо поєднати в одному місці чотири речі:
1) оголошення класу-спадкоємця
2) перевизначення методу
3) оголошення змінної
4) створення об'єкта класу-спадкоємця.
Фактично ми об'єднуємо дві операції – оголошення класу-спадкоємця і створення його об'єкта:
Без анонімного класу | З використанням анонімного класу |
---|---|
|
|
Ще раз розберемо синтаксис:
Thread thread = new Thread();
Thread thread = new Thread()
{
};
Зверни увагу – ми не просто оголошуємо новий клас – ми створюємо змінну – наприкінці ставиться крапка з комою!
— А якщо ми хочемо перевизначити метод run, потрібно писати так:
Thread thread = new Thread()
{
public void run()
{
System.out.println("new run-method");
}
};
— Швидко вчишся – молодець!
— Дякую. А якщо мені ще потрібні методи, яких немає у класу Thread?
— Ти можеш їх дописати.
Це ж повноцінний внутрішній клас, хоч і анонімний:
Код на Java | Опис |
---|---|
|
Червоним кольором позначено код створення змінної.
Зеленим – створення об'єкта. Синім – код анонімного класу-спадкоємця. |
— Повноцінний внутрішній клас?
Тобто я можу і змінні зовнішнього класу використовувати?
— Так, звісно можеш.
— А до конструктора я можу щось передавати?
— Так, але лише параметри конструктора базового класу:
Клас | Об'єкт анонімного внутрішнього класу |
---|---|
|
|
Ми не можемо додати свої параметри до чужого конструктора. Зате можемо використовувати змінні зовнішнього класу – це дуже компенсує цей недолік.
— А якщо мені все ж таки дуже потрібно додати до конструктора ще параметри?
— Тоді оголоси звичайний неанонімний внутрішній клас та використовуй його.
— Справді, я мало не забув про це.
А якщо я оголошу статичну змінну, тоді анонімний клас буде вкладеним, а не внутрішнім — тобто без посилання на внутрішній клас?
— Ні. Буде саме анонімний внутрішній клас. Дивись приклади:
З анонімним класом | Без анонімного класу |
---|---|
|
|
|
|
— Зрозуміло. Статичною стає лише змінна, але не клас.
— Так.
Насправді, у процесі компіляції Компілятор створює внутрішні класи для всіх анонімних внутрішніх класів. Такі класи зазвичай отримують імена «1», «2», «3», тощо.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ