Анонимни вътрешни класове и примери - 1

„Здрасти, Амиго!“

— Но ние вече се поздравихме, Ели!

"Хей, не се карай с леля си. В 31 век, ако не си виждал някого повече от половин час, е обичайно да му кажеш здравей пак. Така че не ми издавай отношението си!"

„Както и да е, време е за друга интересна тема: възпроизвеждането на роботи!“

"O_O."

„Шегувам се, новата тема е анонимни вътрешни класове .“

„В Java понякога има ситуации, в които ще ви е необходим клас, за да наследите няколко класа. Тъй като Java не поддържа множествено наследяване, те са решor този проблем с помощта на вътрешни класове: в нашия клас ние декларираме вътрешен клас и правим той наследява Howъвто и да е клас, който трябва да наследи. Ето един пример:"

Пример за вътрешен клас, който наследява класа Thread
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 говорих за изобретяването на конструкторите?

Преди конструкторите След конструктори
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
Thread thread = new Thread()
{
 public void run()
 {
  tigerRun();
 }
};

„Виждам, че codeът стана по-компактен, но не разбирам съвсем Howво се случва.“

„Можем да комбинираме четири неща в едно:“

1) декларация на производен клас

2) преодоляване на метода

3) декларация на променлива

4) създаване на екземпляр на производен клас.

„Всъщност това, което правим, е да комбинираме две операции: деклариране на производен клас и създаване на екземпляр на този клас.“

Без анонимен клас С анонимен клас
Cat tiger = new Tiger();

class Tiger extends Cat
{
}
Cat tiger = new Cat()
{
};

„Нека отново да проучим синтаксиса:“

Декларация на променлива Thread
Thread thread = new Thread();
Декларация на променлива, чийто тип е «анонимен клас, който наследява Thread»
Thread thread = new Thread()
{

};

"Имайте предвид, че ние не просто дефинираме нов клас. Ние създаваме променлива - има точка и запетая в края!"

„И ако искаме да заменим метода run, тогава трябва да напишем това:“

Декларация на променлива Thread
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

"Бързо схващаш. Браво!"

„Благодаря. Ами ако имаме нужда от други методи, които не са част от класа Thread?“

— Можете да ги напишете.

„Макар и анонимен, това е пълнопequals вътрешен клас:“

Java code Описание
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};	 
Червено: code за създаване на променливата.

Зелено: code за създаване на обекта.

Синьо: code за анонимния производен клас.

„Пълноценна вътрешна класа?“

„Така че мога да използвам променливите на външния клас?“

— Абсолютно.

„И мога ли да предам нещо на конструктора?“

„Да, но само аргументите за конструктора на суперкласа:“

Клас Екземпляр на анонимен вътрешен клас
class Cat
{
 int x, y;
 Cat(int x, int y)
 {
  this.x = x;
  thix.y = y;
 }
}
Cat cat = new Cat(3,4)
{
  public void print()
  {
   System.out.println(x+" "+y);
  }
};

„Не можем да добавим наши собствени параметри към нечий друг конструктор. Но можем да използваме променливите на външния клас, което добре компенсира този недостатък.“

„Ами ако все пак наистина трябва да добавя други параметри към конструктора?“

„След това декларирайте обикновен (неанонимен) вътрешен клас и го използвайте.“

— Добре, почти забравих за това.

"Ами ако декларирам статична променлива? Това ще направи ли анонимния клас статичен вложен клас, а не вътрешен клас? С други думи, ще му липсва ли препратка към външния клас?"

„Не. Това би бил анонимен вътрешен клас. Вижте тези примери.“

С анонимен клас Без анонимен клас
Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
static Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
static TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}

„Разбирам. Само статичната променлива ще бъде статична, а не класът.“

— Да.

"Всъщност компилаторът създава вътрешни класове за всички анонимни вътрешни класове. Тези класове обикновено се наричат ​​«1», «2», «3» и т.н.»