36 votes

Quel est le mal à l'aide de la classe Anonyme?

La question a été soulevée lors de la lecture d'une réponse à cette question - Comment puis-je joindre les deux listes en java. Cette réponse a donné la solution

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };

La lecture des commentaires, les utilisateurs ont dit qu'il était mauvais et laid, et ne doit pas être utilisée en production.

Je voudrais savoir quel est le mal à l'aide de cette? Pourquoi est-il laid, mal ou le mal de l'utiliser dans la production?


Remarque: Posé comme question parce que, le post référencé est trop vieux (2008) et le répondeur est absent depuis plusieurs mois.

51voto

Natix Points 4421

Sauf pour le cas déjà mentionné des questions concernant le bon style de programmation et l'héritage de l'abus, il y a un problème plus subtil - les classes internes (non statique) anonyme des instances de classe loi que les fermetures. Cela signifie qu'ils garder une référence implicite à l'instance de la classe englobante. Cela peut entraîner dans la prévention de la collecte des ordures et à la fin, une fuite de mémoire.

Étant donné un exemple de morceau de code source:

public interface Inner {
    void innerAction();
}

public class Outer {

    public void methodInOuter() {}

    private Inner inner = new Inner() {
        public void innerAction() {
            // calling a method outside of scope of this anonymous class
            methodInOuter();  
        }
    }
}

Ce qui se passe au moment de la compilation, le compilateur crée un fichier de classe pour la nouvelle anonyme sous-classe de la Inner qui reçoit un soi-disant terrain synthétique avec la référence à l'instance de l' Outer classe. Le bytecode généré sera à peu près équivalent à quelque chose comme ceci:

public class Outer$1 implements Inner {

    private final Outer outer; // synthetic reference to enclosing instance

    public Outer$1(Outer outer) {
        this.outer = outer;
    }

    public void innerAction() {
        // the method outside of scope is called through the reference to Outer
        outer.methodInOuter();
    }
}

Une telle capture de référence pour l'affichage de l'instance qui se passe, même pour les classes qui ne sont jamais vraiment accéder à des méthodes ou à des champs de la classe englobante, comme le double-brace initialisé (DBI) de la liste dans votre question.

De ce fait, que le DBI liste conserve une référence à l'instance englobante aussi longtemps qu'elle existe, la prévention de la enfermant instance d'être nettoyée. Supposons que le DBI liste arrive à vivre pour une longue période dans l'application, par exemple en tant que partie du modèle dans le modèle MVC, et la capture de classe englobante est par exemple un JFrame, ce qui est une assez grande classe avec beaucoup de champs. Si vous avez créé un couple de DBI listes, vous obtiendrez une fuite de mémoire très rapidement.

Une solution possible serait d'utiliser DBI seulement en statique méthodes, car il n'y a pas une telle instance englobante disponibles dans leur champ d'application.

D'autre part, j'ai toujours fait valoir que l'utilisation DBI est toujours pas nécessaire dans la plupart des cas. Comme pour la liste de rejoindre, je voudrais créer un simple réutilisables méthode, qui est non seulement plus sûre, mais aussi plus concis et clair.

public static <T> List<T> join(List<? extends T> first, List<? extends T> second) {
    List<T> joined = new ArrayList<>();
    joined.addAll(first);
    joined.addAll(second);
    return joined;
}

Et puis le code du client devient simplement:

List<String> newList = join(listOne, listTwo);

Pour en savoir plus: http://stackoverflow.com/a/924536/1064809

20voto

dasblinkenlight Points 264350

Le "laid" et "ne pas utiliser en production" commentaires se réfèrent à cette utilisation spécifique de l'anonymat de classes, de ne pas anonyme des classes en général.

Cette utilisation spécifique attribue newList anonyme sous-classe de la ArrayList<String>, une toute nouvelle classe créée avec un seul objectif en tête - à savoir, l'initialisation d'une liste avec le contenu des deux listes. Ce n'est pas très lisible (même un lecteur expérimenté serait passer quelques secondes à essayer de le comprendre), mais plus important encore, il peut être atteint sans sous-classement dans le même nombre d'opérations.

Essentiellement, la solution de paie pour un petit commodité avec la création d'une nouvelle sous-classe, ce qui peut entraîner des problèmes sur la route, par exemple, dans les situations lorsque vous essayez de conserver cette collection en utilisant un système automatisé de cadre qui attend les collections d'avoir des types spécifiques.

16voto

Joachim Sauer Points 133411

Cette utilisation particulière de anonyme classes a plusieurs problèmes:

  1. c'est un peu connue de l'idiome. Les développeurs qui ne le savent pas (ou ne connaissent un ne l'utilise pas beaucoup) sera ralentie lors de la lecture et/ou de la modification de code qui l'utilise.
  2. c'est en fait un mauvais usage de la langue: vous n'êtes pas d'essayer de définir un nouveau type d' ArrayList, vous voulez juste quelques liste de tableau avec des valeurs qu'il
  3. il crée une nouvelle classe qui prend des ressources: l'espace disque nécessaire pour la définition de la classe, le temps de l'analyser/vérifier/... c', permgen de tenir la définition de la classe, ...
  4. même si le "vrai" code est un peu plus long, il peut facilement être déplacé dans un bien-nommée méthode utilitaire (joinLists(listOne, listTwo))

À mon avis #1 est la raison la plus importante pour l'éviter, suivi de près par le n ° 2. #3 n'est généralement pas que beaucoup d'un problème, mais ne doit pas être oublié.

4voto

Thomas W Points 7179

Parce que vous n'avez pas besoin d'un autre sous - classe, vous avez juste besoin de créer une nouvelle liste de tableaux de la classe normale, et addAll() les deux listes en elle.

Comme suit:

public static List<String> addLists (List<String> a, List<String> b) {
    List<String> results = new ArrayList<String>();
    results.addAll( a);
    results.addAll( b); 
    return results;
}

C'est un mal pour créer une sous-classe, qui n'est pas nécessaire. Vous n'avez pas besoin de prolonger ou de la sous-classe de comportement il suffit de changer les valeurs des données.

2voto

Erik Pragt Points 3001

Ce n'est pas une mauvaise approche en soi, disons, à la performance ou quelque chose comme ça, mais l'approche est un peu obscur et lors de l'utilisation de quelque chose comme cela, vous avez toujours (disons 99%) ont d'expliquer cette approche. Je pense que c'est l'une des plus grandes raisons de ne pas utiliser cette approche, et tout en tapant:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);

est un peu plus saisissant, c'est un peu plus facile à lire, ce qui l'aide beaucoup dans la compréhension ou de débogage de code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X