Wewnętrzne anonimowe klasy, przykłady - 1

Witaj Amigo!

- Więc witaj już, Ellie!

Więc nie kłóć się z ciocią. W 31 wieku zwyczajowo wita się ponownie, jeśli nie widziało się osoby przez ponad pół godziny. Więc się nie pokazuj!

Tak więc nowym interesującym tematem jest reprodukcja robotów!

— O_O.

— Żartuję, nowym tematem są anonimowe klasy zagnieżdżone.

Czasami w Javie dochodzi do sytuacji, w której trzeba odziedziczyć klasę z kilku klas. Ponieważ wielokrotne dziedziczenie klas w Javie jest zabronione, problem ten rozwiązuje się za pomocą klas wewnętrznych: w klasie, której potrzebujemy, deklarujemy klasę wewnętrzną i dziedziczymy ją z klasy wymaganej. Przykład:

Przykład wewnętrznej klasy odziedziczonej z 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();
  }
 }
}

Spójrzmy na ten przykład:

Potrzebujemy klasy, która dziedziczy po Thread, aby zastąpić jej metodę run.

W tym celu wewnątrz klasy Tiger zadeklarowaliśmy wewnętrzną klasę TigerThread , którą odziedziczyliśmy po Thread i której metoda run została nadpisana.

Dla naszej wygody zadeklarowaliśmy dwie metody w klasie Tiger (analogi metod run i start klasy Thread), metody tigerRun i startTiger .

W metodzie startTiger tworzymy obiekt typu TigerThread i wywołujemy na nim metodę start().

Maszyna Java utworzy wtedy nowy wątek, który rozpocznie się od wywołania metody run klasy TigerThread .

Ta metoda z kolei wywoła naszą metodę run , metodę tigerRun .

- Z nitkami miałem już do czynienia, więc wydaje się to niezbyt trudne.

Czy konieczne jest wywoływanie metod tigerRun i startTiger?

- Nie, można było wywołać run i start , ale chciałem dodatkowo pokazać, że nie dziedziczymy po Thread, poza tym można by się bardziej pogubić w moim wyjaśnieniu.

- OK. Wtedy wszystko wydaje się jasne. Dopiero przy drugim wywołaniu metody startTiger utworzymy kolejną klasę Thread i ją uruchomimy. Czy nie okaże się, że mamy „jeden tygrys pobiegnie w dwóch różnych wątkach”?

- Cóż, masz wielkie oczy. Zgadzam się, nie jest dobrze. Następnie przepiszmy kod w ten sposób:

Kod
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();
  }
 }
}

- Nie tak wspaniale. W każdym razie nie możesz wywołać tej metody dwukrotnie. Ale tym razem przynajmniej nie będziemy zakładać drugiego wątku i udawać, że wszystko jest w porządku.

- Zgadza się, uruchomiłem go po raz drugi, tygrys - zdobądź wyjątek.

„Widzę wszystkie błędy lepiej niż ty, Ellie!”

Dobrze zrobiony. Następnie przejdźmy do anonimowych klas wewnętrznych.

Zwróć uwagę na kilka aspektów powyższego kodu:

1) Odziedziczyliśmy po klasie Thread, ale tak naprawdę nie dodaliśmy tam żadnego kodu. Raczej musieliśmy dziedziczyć, a nie „odziedziczyliśmy w celu rozszerzenia klasy Thread”

2) Zostanie utworzony tylko jeden obiekt klasy TigerThread.

Te. aby nadpisać jedną metodę i stworzyć jeden obiekt, napisaliśmy całą masę kodu.

Pamiętasz, jak ci opowiadałem o wyglądzie konstruktorów?

Przed wynalazkiem Po wynalazku
TigerThread thread = new TigerThread();

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

- Widzę, że kod stał się bardziej zwarty, ale nie do końca rozumiem, co się stało.

W jednym miejscu możemy połączyć cztery rzeczy:

1) deklaracja klasy pochodnej

2) przesłanianie metody

3) deklaracja zmiennej

4) utworzenie obiektu następcy klasy.

Tak naprawdę łączymy ze sobą dwie operacje – deklarację klasy pochodnej i tworzenie jej obiektu:

Brak anonimowej klasy Korzystanie z anonimowej klasy
Cat tiger = new Tiger();

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

Spójrzmy jeszcze raz na składnię:

Deklarowanie zmiennej typu Thread
Thread thread = new Thread();
Deklarowanie zmiennej typu „anonimowe dziecko klasy wątku”
Thread thread = new Thread()
{

};

Uwaga - nie tylko deklarujemy nową klasę - tworzymy zmienną - średnik jest na końcu!

- A jeśli chcemy przesłonić metodę run, to musimy napisać tak:

Deklarowanie zmiennej typu Thread
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

- Szybko łapiesz - brawo!

- Dziękuję. Co zrobić, jeśli potrzebuję więcej metod, których nie ma klasa Thread?

- Możesz je dodać.

To jest pełnoprawna klasa wewnętrzna, choć anonimowa:

Kod Javy Opis
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};	 
Kod tworzenia zmiennej jest zaznaczony na czerwono.

Zielony - tworzenie obiektów.

Niebieski - kod anonimowego następcy klasy.

- Pełnoprawna klasa wewnętrzna?

Te. Czy mogę również używać zewnętrznych zmiennych klasy?

- Tak oczywiście możesz.

- Czy mogę przekazać coś konstruktorowi?

— Tak, ale tylko parametry konstruktora klasy bazowej:

Klasa Anonimowy obiekt klasy wewnętrznej
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);
  }
};

Nie możemy dodawać własnych parametrów do czyjegoś konstruktora. Ale możemy użyć zmiennych klasy zewnętrznej - to dość mocno rekompensuje tę wadę.

— A jeśli nadal naprawdę muszę dodać więcej parametrów do konstruktora?

„Następnie zadeklaruj normalną nieanonimową klasę wewnętrzną i użyj jej.

Rzeczywiście, prawie o tym zapomniałem.

A jeśli zadeklaruję zmienną statyczną, klasa anonimowa zostanie zagnieżdżona, a nie wewnętrzna - tj. bez odniesienia do klasy zewnętrznej?

- NIE. Będzie anonimowa klasa wewnętrzna. Zobacz przykłady:

Z anonimową klasą Brak anonimowej klasy
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();
 }
}

- Jasne. Tylko zmienna staje się statyczna, a nie klasa.

- Tak.

W rzeczywistości podczas kompilacji kompilator generuje klasy wewnętrzne dla wszystkich anonimowych klas wewnętrznych. Takie klasy są zwykle nazywane „1”, „2”, „3” itp.