„Здрасти, Амиго!“
— Но ние вече се поздравихме, Ели!
"Хей, не се карай с леля си. В 31 век, ако не си виждал някого повече от половин час, е обичайно да му кажеш здравей пак. Така че не ми издавай отношението си!"
„Както и да е, време е за друга интересна тема: възпроизвеждането на роботи!“
"O_O."
„Шегувам се, новата тема е анонимни вътрешни класове .“
„В Java понякога има ситуации, в които ще ви е необходим клас, за да наследите няколко класа. Тъй като Java не поддържа множествено наследяване, те са решor този проблем с помощта на вътрешни класове: в нашия клас ние декларираме вътрешен клас и правим той наследява Howъвто и да е клас, който трябва да наследи. Ето един пример:"
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, за да заменим неговия метод за изпълнение."
„Ето защо в класа Tiger декларирахме вътрешния клас TigerThread , който наследява Thread и замества метода run.
„За удобство дефинирахме два метода в класа Tiger: tigerRun и startTiger (които са аналогични на методите run и start на Thread.“
„В метода tigerStart създаваме обект TigerThread и извикваме неговия метод start().“
„JVM ще създаде нова нишка, която ще започне да се изпълнява, когато се извика методът за изпълнение на TigerThread .“
„След това този метод извиква нашия метод за изпълнение : tigerRun .“
„Работил съм с нишки преди, така че това изглежда лесно.“
„Трябва ли да именуваме методите tigerRun и tigerStart?“
„Не, можехме да ги наречем run и start, но също така исках да демонстрирам, че не наследяваме Thread. Едно обяснение може да е по-объркващо.“
„ОК. Тогава мисля, че го разбирам. Но ако tigerStart бъде извикан втори път, ще създадем и стартираме втори обект Thread. Това не означава ли, че ще имаме «един тигър, работещ на две различни нишки»? "
„Е, не си ли остър! Прав си, а това не е добре. Нека пренапишем codeа така:“
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();
}
}
}
„Не е съвсем перфектно. Все още не можете да извикате метод като този два пъти. Но този път поне няма да създадем втора нишка и да изглежда, че всичко е наред.“
„Така е. При втория път, когато стартирате Тигър, ще получите изключение.“
— Вече забелязвам грешките по-добре от теб, Ели!
„Да, справяте се страхотно. Тогава нека преминем към анонимни вътрешни класове.“
„Обърнете внимание на няколко аспекта на codeа по-горе:“
1) Наследихме класа Thread, но практически не внедрихме code там. „По-скоро „трябваше да наследим класа Thread“, отколкото „ние го наследихме, за да го разширим“.
2) Ще бъде създаден само един обект TigerThread.
С други думи, написахме цял куп code само за да заменим един метод и да създадем един обект.
Спомняте ли си How говорих за изобретяването на конструкторите?
Преди конструкторите | След конструктори |
---|---|
|
|
„Виждам, че codeът стана по-компактен, но не разбирам съвсем Howво се случва.“
„Можем да комбинираме четири неща в едно:“
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?“
— Можете да ги напишете.
„Макар и анонимен, това е пълнопequals вътрешен клас:“
Java code | Описание |
---|---|
|
Червено: code за създаване на променливата.
Зелено: code за създаване на обекта. Синьо: code за анонимния производен клас. |
„Пълноценна вътрешна класа?“
„Така че мога да използвам променливите на външния клас?“
— Абсолютно.
„И мога ли да предам нещо на конструктора?“
„Да, но само аргументите за конструктора на суперкласа:“
Клас | Екземпляр на анонимен вътрешен клас |
---|---|
|
|
„Не можем да добавим наши собствени параметри към нечий друг конструктор. Но можем да използваме променливите на външния клас, което добре компенсира този недостатък.“
„Ами ако все пак наистина трябва да добавя други параметри към конструктора?“
„След това декларирайте обикновен (неанонимен) вътрешен клас и го използвайте.“
— Добре, почти забравих за това.
"Ами ако декларирам статична променлива? Това ще направи ли анонимния клас статичен вложен клас, а не вътрешен клас? С други думи, ще му липсва ли препратка към външния клас?"
„Не. Това би бил анонимен вътрешен клас. Вижте тези примери.“
С анонимен клас | Без анонимен клас |
---|---|
|
|
|
|
„Разбирам. Само статичната променлива ще бъде статична, а не класът.“
— Да.
"Всъщност компилаторът създава вътрешни класове за всички анонимни вътрешни класове. Тези класове обикновено се наричат «1», «2», «3» и т.н.»
GO TO FULL VERSION