J'ai une classe, appelons-la LineGraph, qui rend un graphique linéaire. Je dois le sous-classer, mais la classe dérivée n’est utilisée qu’à un seul endroit et est couplée à la classe qui l’utilise. Donc, j'utilise une classe intérieure.
Je vois deux façons de faire cela:
Classe intérieure anonyme
public class Gui {
LineGraph graph = new LineGraph() {
// extra functionality here.
};
}
Nommé classe intérieure
public class Gui {
MyLineGraph graph = new MyLineGraph();
private class MyLineGraph extends LineGraph {
// extra functionality here.
}
}
Je ne suis pas un fan des classes inférieures anonymes, parce que franchement, je pense que c'est vraiment moche. Mais dans le cas d'une sous-classe qui n'est utilisée qu'à un seul endroit, une classe interne nommée est-elle excessive? Quelle est la pratique acceptée?
L'un des avantages des classes internes anonymes est que personne ne peut l'utiliser nulle part ailleurs, alors qu'une classe interne nommée peut être utilisée (ne serait-ce que par la classe qui l'a créée si elle est privée). C'est une petite distinction, mais cela signifie que vous pouvez protéger une classe interne contre une utilisation accidentelle ailleurs.
En outre, l’utilisation de la classe interne anonyme donne à tous ceux qui lisent votre code un peu de tête - "cette classe est utilisée uniquement ici et nulle part ailleurs". Si vous voyez une classe interne nommée, quelqu'un pourrait penser qu'elle serait utilisée à plusieurs endroits de la classe.
Ils sont très similaires, donc aucun des deux n’est décisif. Je pense simplement que la clarté est utile si vous utilisez des classes internes anonymes pour des éléments uniques et que vous les appelez classes internes si elles sont utilisées plusieurs fois dans la classe.
(Contre point à Daniel Lew)
Un inconvénient des classes internes anonymes est que personne ne peut l'utiliser nulle part ailleurs, alors qu'une classe interne nommée peut être utilisée (ne serait-ce que par la classe qui l'a créée si elle est privée). C'est une petite distinction, mais cela signifie que vous pouvez aider à vous assurer qu'une classe interne n'est pas recréée par accident ailleurs.
En outre, l'utilisation de la classe interne anonyme confère plus de difficultés à quiconque lit votre code, car il doit alors analyser cette classe sortie de nulle part. En utilisant une classe interne nommée, vous pouvez organiser davantage la source.
J'ai vu des cas où il y a deux classes internes anonymes (ou plus) avec exactement le même code. Dans les interfaces graphiques en particulier (où vous pouvez avoir plusieurs contrôles effectuant la même action), cela peut apparaître (et je parle de code de production, pas de code écrit par mes étudiants).
Le problème de la lisibilité va dans les deux sens, certaines personnes trouvent mieux les classes internes anonymes car elles vous permettent de voir ce qui se passe dans un seul lieu, d’autres le trouvent distrait. Cette partie dépend des préférences personnelles.
Rendre une classe statique est également plus efficace, si vous déclarez une classe interne anonyme dans une instance, il y aura plus de temps système, ce qui, si vous n'avez pas besoin d'accéder aux variables d'instance, est une perte de temps (mais ne vous inquiétez probablement pas. jusqu'à ce que cela pose problème).
Ma préférence personnelle est d'utiliser des classes non anonymes, car elles permettent plus de flexibilité lorsque le code est modifié ultérieurement.
Pourquoi avez-vous besoin de le sous-classer? S'il s'agit simplement de remplacer une méthode virtuelle existante, je pense qu'une classe interne anonyme est acceptable. Si vous ajoutez des fonctionnalités supplémentaires, j'utiliserais une classe nommée. J'en ferais cependant une classe imbriquée (c'est-à-dire avec le modificateur static
) - je les trouve plus facile à raisonner à propos de :)
Faites la chose la plus simple qui puisse fonctionner : Utilisez la classe interne anonyme.
Si vous vous rendez compte par la suite que vous avez besoin d'une portée plus large, refacturez le code pour prendre en charge cette opération.
(Vous feriez la même chose avec les variables - en les plaçant dans la portée la plus spécifique. Il est logique de faire la même chose avec d'autres actifs source.)
Les classes internes anonymes sont difficiles à déboguer dans Eclipse (c'est ce que j'utilise). Vous ne pourrez pas regarder les valeurs de variables/injecter des valeurs en cliquant simplement avec le bouton droit de la souris.
Ma règle personnelle: si la classe interne anonyme doit être petite, restez-en avec une classe anonyme. Petit étant défini comme comprenant environ 20 à 30 lignes ou moins . S'il doit être plus long, il commence à être illisible à mon avis, je le fais donc comme une classe interne nommée . Je me souviens avoir vu une fois 4000 + ligne interne anonyme.
Un inconvénient des classes internes est qu'elles ne peuvent pas être statiques. Cela signifie qu'il contiendra une référence à la classe externe qui les contient.
Les classes internes non statiques peuvent poser problème. Par exemple, une classe interne a récemment été sérialisée, mais la classe externe n'était pas sérialisable. La référence cachée signifiait que la classe externe serait également sérialisée, ce qui a bien sûr échoué, mais il a fallu un certain temps pour savoir pourquoi.
Là où je travaille, les classes internes statiques sont encouragées dans nos meilleures pratiques de codage (dans la mesure du possible) car elles contiennent moins de bagages cachés et sont plus minces.
Les classes anonymes ne peuvent pas avoir de constructeur car elles n'ont pas de nom. Si vous devez transmettre d'autres variables que celles du ou des constructeurs de la classe que vous étendez, vous devez utiliser une classe interne (statique) nommée. Cela peut parfois être surmonté en utilisant des variables finales dans la méthode/le code qui l’entoure, mais c’est un peu moche à mon humble avis (et peut conduire à ce que Robin a dit).
Les classes internes anonymes seraient généralement la voie à suivre. Je les trouve très lisibles. Cependant, si l'instance de cette classe a besoin d'être sérialisée (même si c'est uniquement un champ d'autre chose), je vous recommande fortement d'utiliser des classes internes nommées. Les noms des classes internes anonymes dans le bytecode peuvent changer très facilement, ce qui peut interrompre la sérialisation.
Cours anonymes:
static
dans la définition (classe statique, champ statique, initialiseur statique, etc.)Foo$1 myFoo=new Foo$1(){int x=0;}
ne fonctionne pas)Foo$1
ne fonctionne pas pour moi)Les classes anonymes peuvent cependant avoir un initialiseur comme {super.foo(finalVariable+this.bar);}
Les classes internes nommées n'ont pas ces limitations, mais même si l'une d'entre elles est utilisée exclusivement au milieu d'une procédure longue, la déclaration devra être déplacée vers la classe nommée suivante.
Personnellement, je préfère les classes internes anonymes si les restrictions ne s'appliquent pas pour les raisons suivantes:
Je n'ai pas de problème avec les classes anonymes simples. Mais si elle comporte plus de quelques lignes de code ou plusieurs méthodes, une classe interne est plus claire. Je pense aussi que dans certaines conditions, ils ne devraient jamais être utilisés. Comme quand ils doivent renvoyer des données.
J'ai vu du code dans lequel un tableau final d'un élément est utilisé pour transmettre les données d'un appel à une classe interne anonyme. Dans la méthode de la classe anon, l'élément unique est défini, puis ce "résultat" est extrait une fois la méthode terminée. Code valide, mais moche.
Je viens d'avoir cette discussion aujourd'hui avec mon collègue et j'étais en train de fouiller pour avoir l'opinion populaire.
Je suis d'accord avec TofuBeer. Si votre code comporte plus de 2 lignes, il ne s'agit probablement pas d'un «élément unique» et peut être réutilisé un jour. Si vous créez un formulaire en utilisant un modèle MVC, vous pouvez avoir 20 éléments contrôlables sur une page au lieu d'encombrer la vue avec des tonnes de classes anonymes (votre vue a probablement été créée à l'aide d'un générateur d'interface graphique et le code a été généré automatiquement éditer la source même pas une option) vous aurez probablement alimenter chaque élément à un contrôleur. Vous pouvez imbriquer des classes internes dans le contrôleur pour gérer chaque interface de gestionnaire/écouteur différente requise par votre vue. Cela organise très bien le code, en particulier si vous avez une convention stipulant que votre classe de gestionnaires doit être nommée à l'aide du nom de l'élément d'interface graphique (comme backButtonHandler pour un backButtonElement). Cela a très bien fonctionné pour nous et nous avons écrit un outil d'enregistrement automatique (lorsque vous enregistrez un contrôleur dans une vue, la vue recherche les classes internes à l'aide du nom de l'élément et les utilise pour un gestionnaire sur chaque élément nommé). Cela ne serait pas possible avec des classes anonymes et rend le contrôleur beaucoup plus recyclable.
TLDR: en cas de doute, écrivez une classe intérieure nommée. Vous ne saurez jamais si quelqu'un souhaite réutiliser votre code un jour (à moins que ce ne soit deux lignes, vous devez vous demander «ce code sent-il?»). Un code bien organisé présente des avantages bien supérieurs à long terme, en particulier lorsque votre projet est en maintenance.
Je pense que ce que vous avez fait est parfaitement logique dans ce cas. De toute façon, si vous le regardez, vous coupez les cheveux en quatre avec ce problème particulier. Ils sont tous les deux si semblables et l'un ou l'autre fonctionnera.
Avec les classes internes anonymes, nous ne pouvons pas modifier l'état des membres de la classe incluse. Ils doivent être déclarés comme final.
Je pense que c'est une question de goût. Je préfère utiliser des classes anonymes pour Functors . Mais dans votre cas, j'utiliserais une classe interne, car je pense que vous allez stocker plus que quelques lignes de code, peut-être plus qu'une simple méthode. Et cela soulignerait le fait qu'il ajoute des fonctionnalités à la super-classe, en les plaçant dans la classe, plutôt que cachées quelque part dans une méthode. De plus, qui sait, peut-être qu'un jour vous aurez peut-être besoin de la sous-classe else où. Bien entendu, cela dépend de votre connaissance de l’évolution de votre logiciel. Autre que cela, il suffit de lancer une pièce de monnaie. :)