Classes internes anonymes et exemples - 1

« Salut Amigo ! »

« Mais nous nous sommes déjà dit bonjour, Ellie !

"Hé, ne discute pas avec ta tante. Au 31e siècle, si tu n'as pas vu quelqu'un depuis plus d'une demi-heure, il est de coutume de dire bonjour à nouveau. Alors ne me donne pas ton attitude!"

"Quoi qu'il en soit, il est temps d'aborder un autre sujet intéressant : la reproduction de robots !"

"O_O."

"Je plaisante, le nouveau sujet est les classes internes anonymes ."

"En Java, il y a parfois des situations où vous aurez besoin d'une classe pour hériter de plusieurs classes. Comme Java ne prend pas en charge l'héritage multiple, ils ont résolu ce problème en utilisant des classes internes : dans notre classe, nous déclarons une classe interne et faisons il hérite de la classe dont nous avons besoin. Voici un exemple :"

Exemple de classe interne qui hérite de la classe 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();
  }
 }
}

"Prenons un autre exemple :"

Nous avons besoin d'une sous-classe de la classe Thread pour remplacer sa méthode d'exécution."

"C'est pourquoi, dans la classe Tiger, nous avons déclaré la classe interne TigerThread , qui hérite de Thread et remplace la méthode run.

"Pour plus de commodité, nous avons défini deux méthodes dans la classe Tiger : TigerRun et startTiger (qui sont analogues aux méthodes run et start de Thread."

"Dans la méthode tigerStart, nous créons un objet TigerThread et invoquons sa méthode start()."

"La JVM créera un nouveau thread qui commencera à s'exécuter lorsque la méthode d'exécution de TigerThread sera appelée."

"Cette méthode appelle ensuite notre méthode d'exécution : tigerRun ."

"J'ai déjà travaillé avec des threads, donc cela semble simple."

« Doit-on nommer les méthodes tigerRun et tigerStart ?

"Non, nous aurions pu les appeler run and start, mais je voulais aussi démontrer que nous n'héritons pas de Thread. Une explication aurait peut-être été plus confuse."

"OK. Alors je pense que j'ai compris. Mais si TigerStart est appelé une deuxième fois, nous créerons et démarrerons un deuxième objet Thread. Cela ne signifie-t-il pas que nous aurons "un tigre s'exécutant sur deux threads différents" ? "

"Eh bien, n'êtes-vous pas pointu ! Vous avez raison, et ce n'est pas bon. Réécrivons le code comme ceci : "

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

"Ce n'est pas tout à fait parfait. Vous ne pouvez toujours pas appeler une méthode comme celle-là deux fois. Mais cette fois, au moins, nous ne créerons pas de deuxième thread et donnerons l'impression que tout va bien."

"C'est vrai. La deuxième fois qu'un Tiger est démarré, vous obtiendrez une exception."

« Je repère déjà les erreurs mieux que toi, Ellie !

« Ouais, tu t'en sors très bien. Alors passons aux classes internes anonymes.

"Notez plusieurs aspects du code ci-dessus :"

1) Nous avons hérité de la classe Thread, mais n'y avons pratiquement pas implémenté de code. "C'était plus "nous avons dû hériter de la classe Thread" plutôt que "nous en avons hérité pour l'étendre".

2) Un seul objet TigerThread sera créé.

En d'autres termes, nous avons écrit tout un tas de code juste pour remplacer une méthode et créer un objet.

Vous souvenez-vous comment j'ai parlé de l'invention des constructeurs ?

Avant les constructeurs Après les constructeurs
TigerThread thread = new TigerThread();

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

"Je vois que le code est devenu plus compact, mais je ne comprends pas très bien ce qui se passe."

"Nous pouvons combiner quatre choses en une :"

1) déclaration d'une classe dérivée

2) substitution de méthode

3) déclaration d'une variable

4) création d'une instance d'une classe dérivée.

"En fait, ce que nous faisons, c'est combiner deux opérations : déclarer une classe dérivée et créer une instance de cette classe."

Sans classe anonyme Avec classe anonyme
Cat tiger = new Tiger();

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

"Explorons à nouveau la syntaxe :"

Déclaration d'une variable Thread
Thread thread = new Thread();
Déclaration d'une variable dont le type est "une classe anonyme qui hérite de Thread"
Thread thread = new Thread()
{

};

"Notez que nous ne définissons pas simplement une nouvelle classe. Nous créons une variable - il y a un point-virgule à la fin !"

"Et si nous voulons remplacer la méthode run, nous devons écrire ceci :"

Déclaration d'une variable Thread
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

"Tu comprends vite. Bravo !"

« Merci. Et si nous avions besoin d'autres méthodes qui ne font pas partie de la classe Thread ? »

"Vous pouvez les écrire."

« Bien qu'anonyme, il s'agit d'une classe intérieure à part entière : »

Code Java Description
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};	 
Rouge : code de création de la variable.

Vert : code de création de l'objet.

Bleu : code pour la classe dérivée anonyme.

« Une classe intérieure à part entière ? »

"Je peux donc utiliser les variables de la classe externe ?"

"Absolument."

« Et je peux passer quelque chose au constructeur ?

"Oui, mais uniquement les arguments du constructeur de la superclasse :"

Classe Instance d'une classe interne anonyme
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);
  }
};

"Nous ne pouvons pas ajouter nos propres paramètres au constructeur de quelqu'un d'autre. Mais nous pouvons utiliser les variables de la classe externe, ce qui compense bien cette lacune."

"Et si j'ai encore vraiment besoin d'ajouter d'autres paramètres au constructeur ?"

"Ensuite, déclarez une classe interne ordinaire (non anonyme) et utilisez-la."

« C'est vrai, j'ai presque oublié ça.

"Et si je déclare une variable statique ? Cela ferait-il de la classe anonyme une classe imbriquée statique plutôt qu'une classe interne ? En d'autres termes, manquera-t-il de référence à la classe externe ? »

"Non. Ce serait une classe interne anonyme. Regardez ces exemples."

Avec classe anonyme Sans classe anonyme
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();
 }
}

"Je vois. Seule la variable statique serait statique, pas la classe."

"Ouais."

"En fait, le compilateur crée des classes internes pour toutes les classes internes anonymes. Ces classes sont généralement nommées « 1 », « 2 », « 3 », etc. »