Classes intérieures

Q: Alors, à quoi servent les classes internes?

R: Les classes internes s'imbriquent dans d'autres classes. Une classe normale est un membre direct d'un package, une classe de premier niveau. Les classes internes, qui sont devenues disponibles avec Java 1.1, sont disponibles en quatre versions:

  • Classes de membres statiques
  • Classes de membres
  • Cours locaux
  • Cours anonymes

Jetons un coup d'œil à chacun d'eux à tour de rôle.

En bref, une classe membre statique est un membre statique d'une classe. Comme toute autre méthode statique, une classe membre statique a accès à toutes les méthodes statiques de la classe parente ou de niveau supérieur.

Comme une classe membre statique, une classe membre est également définie en tant que membre d'une classe. Contrairement à la variété statique, la classe membre est spécifique à l'instance et a accès à toutes les méthodes et tous les membres, même la thisréférence du parent .

Les classes locales sont déclarées dans un bloc de code et ne sont visibles que dans ce bloc, comme toute autre variable de méthode.

Enfin, une classe anonyme est une classe locale qui n'a pas de nom.

Pour répondre à votre question spécifique, je vais me concentrer sur les classes internes membres et anonymes, car ce sont celles que vous rencontrerez et utiliserez probablement. Pour moi, les avantages des classes internes peuvent être divisés en trois catégories: un avantage orienté objet, un avantage organisationnel et un avantage de rappel.

L'avantage orienté objet

À mon humble avis, la caractéristique la plus importante de la classe interne est qu'elle vous permet de transformer des choses en objets que vous ne transformeriez normalement pas en objets. Cela permet à votre code d'être encore plus orienté objet qu'il ne le serait sans classes internes.

Regardons la classe des membres. Étant donné que son instance est membre de son instance parent, elle a accès à tous les membres et méthodes du parent. À première vue, cela peut ne pas sembler beaucoup; nous avons déjà ce genre d'accès depuis une méthode de la classe parent. Cependant, la classe membre nous permet de retirer la logique du parent et de l'objectiver. Par exemple, une classe d'arbre peut avoir une méthode et de nombreuses méthodes d'assistance qui effectuent une recherche ou parcourent l'arborescence. D'un point de vue orienté objet, l'arbre est un arbre et non un algorithme de recherche. Cependant, vous avez besoin d'une connaissance approfondie des structures de données de l'arborescence pour effectuer une recherche.

Une classe interne nous permet de supprimer cette logique et de la placer dans sa propre classe. Donc, d'un point de vue orienté objet, nous avons pris la fonctionnalité là où elle n'appartenait pas et l'avons placée dans sa propre classe. Grâce à l'utilisation d'une classe interne, nous avons réussi à découpler l'algorithme de recherche de l'arbre. Maintenant, pour changer l'algorithme de recherche, nous pouvons simplement permuter dans une nouvelle classe. Je pourrais continuer, mais cela ouvre notre code à de nombreux avantages fournis par les techniques orientées objet.

L'avantage organisationnel

La conception orientée objet n'est pas l'affaire de tous, mais heureusement, les classes internes en fournissent plus. D'un point de vue organisationnel, les classes internes nous permettent d'organiser davantage la structure de notre package grâce à l'utilisation d'espaces de noms. Au lieu de tout vider dans un package plat, les classes peuvent être davantage imbriquées dans des classes. Explicitement, sans classes internes, nous étions limités à la structure hiérarchique suivante:

package1 classe 1 classe 2 ... classe n ... package n 

Avec les classes internes, nous pouvons faire ce qui suit:

paquet 1 classe 1 classe 2 classe 1 classe 2 ... classe n 

Utilisées avec précaution, les classes internes peuvent fournir une hiérarchie structurelle qui s'adapte plus naturellement à vos classes.

L'avantage du rappel

Les classes membres internes et les classes anonymes fournissent toutes deux une méthode pratique pour définir les rappels. L'exemple le plus évident concerne le code GUI. Cependant, l'application du rappel peut s'étendre à de nombreux domaines.

La plupart des interfaces graphiques Java ont une sorte de composant qui déclenche un actionPerformed()appel de méthode. Malheureusement, la plupart des développeurs ont simplement implémenté leur fenêtre principale ActionListener. En conséquence, tous les composants partagent la même actionPerformed()méthode. Pour déterminer quel composant a effectué l'action, il y a normalement un commutateur géant et laid dans la actionPerformed()méthode.

Voici un exemple d'implémentation monolithique:

classe publique SomeGUI étend JFrame implémente ActionListener {protected JButton button1; protégé JButton button2; ... protégé JButton buttonN; public void actionPerformed (ActionEvent e) {if (e.getSource () == button1) {// faire quelque chose} else if (e.getSource () == button2) {... vous obtenez l'image

Chaque fois que vous voyez des interrupteurs ou des gros blocs if/ if elseblocs, des sonnettes d'alarme fortes devraient commencer à sonner dans votre esprit. En général, de telles constructions sont une mauvaise conception orientée objet car une modification dans une section du code peut nécessiter une modification correspondante dans l'instruction switch. Les classes membres internes et les classes anonymes nous permettent de nous éloigner de la actionPerformed()méthode commutée .

Au lieu de cela, nous pouvons définir une classe interne qui implémente ActionListenerpour chaque composant que nous voulons écouter. Cela peut entraîner de nombreuses classes internes. Cependant, nous pouvons éviter de grosses instructions de commutation et avoir l'avantage supplémentaire d'encapsuler notre logique d'action. De plus, cette approche peut améliorer les performances. Dans un commutateur où il y a n comparaisons, on peut s'attendre à n / 2 comparaisons dans le cas moyen. Les classes internes nous permettent de mettre en place une correspondance 1: 1 entre l'acteur d'action et l'auditeur d'action. Dans une grande interface graphique, ces optimisations peuvent avoir un impact considérable sur les performances. Une approche anonyme peut ressembler à ceci:

classe publique SomeGUI étend JFrame {... déclarations de membre de bouton ... protected void buildGUI () {button1 = new JButton (); button2 = nouveau JButton (); ... button1.addActionListener (new java.awt.event.ActionListener () {public void actionPerformed (java.awt.event.ActionEvent e) {// faire quelque chose}}); .. répéter pour chaque bouton

En utilisant des classes membres internes, le même programme ressemblerait à ceci:

classe publique SomeGUI étend JFrame {... déclarations de membre de bouton // définitions de classe interne classe Button1Handler implémente ActionListener {public void actionPerformed (ActionEvent e) {// faire quelque chose}} ... définir une classe de membre interne pour chaque bouton protected void buildGUI () {// initialiser les boutons button1 = new JButton (); button2 = nouveau JButton (); ... // enregistre une instance d'écouteur d'action de classe interne // pour chaque bouton button1.addActionListener (new Button1Handler ()); .. répéter pour chaque bouton

Puisque les classes internes ont accès à tout dans le parent, nous pouvons déplacer toute logique qui serait apparue dans une actionPerformed()implémentation monolithique vers une classe interne.

Je préfère utiliser les classes membres comme rappels. Cependant, c'est une question de préférence personnelle. Je pense juste que trop de classes anonymes encombrent le code. Je pense également que les classes anonymes peuvent devenir lourdes si elles dépassent une ou deux lignes.

Désavantages?

Comme pour toute autre chose, vous devez prendre le bon avec le mauvais. Les classes intérieures ont leurs inconvénients. Du point de vue de la maintenance, les développeurs Java inexpérimentés peuvent trouver la classe interne difficile à comprendre. L'utilisation de classes internes augmentera également le nombre total de classes dans votre code. De plus, du point de vue du développement, la plupart des outils Java sont un peu à court de leur support des classes internes. Par exemple, j'utilise VisualAge pour Java d'IBM pour mon codage quotidien. Alors que les classes internes seront compilées dans VisualAge, il n'y a pas de navigateur ou de modèle de classe interne. Au lieu de cela, vous devez simplement taper la classe interne directement dans la définition de classe. Cela rend malheureusement la navigation dans la classe interne difficile. Il est également difficile de taper car vous perdez beaucoup de VisualAge 'La saisie automatique du code facilite la saisie dans la définition de classe ou l'utilisation d'une classe interne.

Tony Sintes est consultant senior chez ObjectWave, spécialisé dans les télécommunications. Sintes, un programmeur Java 1.1 certifié Sun et développeur Java 2, travaille avec Java depuis 1997.

En savoir plus sur ce sujet

  • "Spécification des classes internes", de Sun, fournit un aperçu détaillé des classes internes

    //java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

Cette histoire, "Inner classes" a été initialement publiée par JavaWorld.