Comment les classes internes anonymes sont-elles utilisées en Java?


309

Quelle est l'utilisation des classes anonymes en Java? Peut-on dire que l'utilisation de la classe anonyme est l'un des avantages de Java?


67
Ce n'est pas tant un avantage de Java que c'est un moyen de contourner le manque de fermetures en Java.
Eric Wilson

4
Java 8 a également introduit des expressions Lambda. Vérifiez la réponse: stackoverflow.com/questions/355167/…
akhil_mittal

Réponses:


369

Par "classe anonyme", je suppose que vous entendez une classe interne anonyme .

Une classe interne anonyme peut s'avérer utile lors de la création d'une instance d'un objet avec certains "extras" tels que des méthodes de substitution, sans avoir à sous-classer réellement une classe.

J'ai tendance à l'utiliser comme raccourci pour attacher un écouteur d'événement:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

L'utilisation de cette méthode rend le codage un peu plus rapide, car je n'ai pas besoin de créer une classe supplémentaire qui implémente ActionListener- je peux simplement instancier une classe interne anonyme sans réellement créer une classe séparée.

J'utilise cette technique uniquement pour les tâches "rapides et sales" où rendre une classe entière semble inutile. Avoir plusieurs classes internes anonymes qui font exactement la même chose devrait être refactorisé en une classe réelle, que ce soit une classe interne ou une classe distincte.


5
Ou vous pouvez refactoriser les classes internes anonymes en double en une seule méthode avec la classe interne anonyme (et éventuellement un autre code dupliqué).
Tom Hawtin - tackline

3
Grande réponse mais une question rapide. Cela signifie-t-il que Java peut vivre sans classes internes anonymes et qu'elles sont une option supplémentaire à choisir?
realPK

5
Très bien expliqué, même difficile, je suggère à quiconque lisant ceci de rechercher et de voir ce que les expressions java 8 et lambda peuvent faire pour rendre le codage plus rapide et plus lisible.
Pievis

2
@ user2190639 Justement, on ne peut pas demander mieux avec Lambda en Java8
bonCodigo

3
pourquoi avez-vous dit overloading methodset non overriding methods?
Tarun

73

Les classes internes anonymes sont en réalité des fermetures, elles peuvent donc être utilisées pour émuler des expressions lambda ou des «délégués». Par exemple, prenez cette interface:

public interface F<A, B> {
   B f(A a);
}

Vous pouvez l'utiliser de manière anonyme pour créer une fonction de première classe en Java. Disons que vous avez la méthode suivante qui renvoie le premier nombre supérieur à i dans la liste donnée, ou i si aucun nombre n'est plus grand:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

Et puis vous avez une autre méthode qui retourne le premier nombre plus petit que i dans la liste donnée, ou i si aucun nombre n'est plus petit:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Ces méthodes sont presque identiques. En utilisant le type de fonction de première classe F, nous pouvons les réécrire en une seule méthode comme suit:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

Vous pouvez utiliser une classe anonyme pour utiliser la méthode firstMatch:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

C'est un exemple vraiment artificiel, mais il est facile de voir qu'être capable de passer des fonctions comme s'il s'agissait de valeurs est une fonctionnalité assez utile. Voir «Votre langage de programmation peut-il le faire» par Joel lui-même.

Une jolie bibliothèque pour programmer Java dans ce style: Java fonctionnel.


20
malheureusement, la verbosité de la programmation fonctionnelle en java, à mon humble avis, surpasse ses gains - l'un des points frappants de la programmation fonctionnelle est qu'elle a tendance à réduire la taille du code et facilite la lecture et la modification. Mais java fonctionnel ne semble pas du tout faire cela.
Chii

27
Toute la compréhensibilité de la programmation fonctionnelle, avec la justesse de Java!
Adam Jaskiewicz

3
D'après mon expérience, le style fonctionnel en Java est payé avec de la verbosité dès le départ, mais il donne de la brièveté à long terme. Par exemple, myList.map (f) est considérablement moins verbeux que la boucle for correspondante.
Apocalisp

2
Scala , un langage de style de programmation fonctionnel, fonctionne prétendument bien à l'intérieur de la JVM et peut être une option pour les scénarios de programmation fonctionnelle.
Darrell Teague

51

La classe interne anonyme est utilisée dans le scénario suivant:

1.) Pour remplacer (sous-classement), lorsque la définition de classe n'est pas utilisable, sauf dans le cas actuel:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Pour la mise en œuvre d'une interface, lorsque la mise en œuvre de l'interface n'est requise que pour le cas actuel:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Classe interne anonyme définie par argument:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Grande réponse, ressemble à 3.) est le modèle utilisé pour les auditeurs d'événements
xdl

47

Je les utilise parfois comme hack de syntaxe pour l'instanciation de la carte:

Map map = new HashMap() {{
   put("key", "value");
}};

contre

Map map = new HashMap();
map.put("key", "value");

Il enregistre une certaine redondance lors de l'exécution de nombreuses instructions put. Cependant, j'ai également rencontré des problèmes lors de cette opération lorsque la classe externe doit être sérialisée via l'accès à distance.


56
Pour être clair, le premier ensemble d'accolades est la classe interne anonyme (sous-classement HashMap). Le deuxième ensemble d'accolades est un initialiseur d'instance (plutôt qu'un statique) qui définit ensuite les valeurs de votre sous-classe HashMap. +1 pour l'avoir mentionné, -1 pour ne pas l'avoir précisé pour les noobs. ;-D
Spencer Kormos

4
En savoir plus sur la syntaxe à double accolade ici .
Martin Andersson


18

Ils sont couramment utilisés comme une forme verbeuse de rappel.

Je suppose que vous pourriez dire qu'ils sont un avantage par rapport à ne pas les avoir et à créer une classe nommée à chaque fois, mais des concepts similaires sont beaucoup mieux implémentés dans d'autres langages (comme les fermetures ou les blocs)

Voici un exemple de swing

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Bien qu'il soit toujours très verbeux, c'est beaucoup mieux que de vous forcer à définir une classe nommée pour chaque auditeur à jeter comme ça (bien que selon la situation et la réutilisation, cela peut toujours être la meilleure approche)


1
Vouliez-vous dire laconique? S'il était verbeux, le rappel resterait séparément, ce qui le rendrait un peu plus grand et le rendrait ainsi verbeux. Si vous dites que c'est encore verbeux, quelle serait alors une forme laconique?
user3081519

1
@ user3081519, quelque chose comme myButton.addActionListener(e -> { /* do stuff here */})ou myButton.addActionListener(stuff)serait terser.
Samuel Edwin Ward

8

Vous l'utilisez dans des situations où vous devez créer une classe à des fins spécifiques dans une autre fonction, par exemple, en tant qu'écouteur, en tant que runnable (pour générer un thread), etc.

L'idée est que vous les appeliez depuis l'intérieur du code d'une fonction afin de ne jamais vous y référer ailleurs, vous n'avez donc pas besoin de les nommer. Le compilateur les énumère simplement.

Ils sont essentiellement du sucre syntaxique et devraient généralement être déplacés ailleurs à mesure qu'ils grossissent.

Je ne sais pas si c'est l'un des avantages de Java, bien que si vous les utilisez (et nous les utilisons tous fréquemment, malheureusement), alors vous pourriez affirmer qu'ils en sont un.


6

GuideLines for Anonymous Class.

  1. La classe anonyme est déclarée et initialisée simultanément.

  2. La classe anonyme doit étendre ou implémenter une et une seule classe ou interface resp.

  3. Comme la classe anonymouse n'a pas de nom, elle ne peut être utilisée qu'une seule fois.

par exemple:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Concernant # 3: Pas entièrement vrai. Vous pouvez acquérir plusieurs instances d'une classe anonyme avec réflexion, par exemple ref.getClass().newInstance().
icza

Les règles ne répondent pas à la question.
Marquis de Lorne

5

Oui, les classes internes anonymes sont certainement l'un des avantages de Java.

Avec une classe interne anonyme, vous avez accès aux variables finales et membres de la classe environnante, ce qui est pratique pour les auditeurs, etc.

Mais un avantage majeur est que le code de classe interne, qui est (au moins devrait être) étroitement couplé à la classe / méthode / bloc environnante, a un contexte spécifique (la classe, la méthode et le bloc environnants).


1
Avoir accès à la classe environnante est très important! Je pense que c'est la raison dans de nombreux cas où une classe anonyme est utilisée, car elle a besoin / utilise les attributs, les méthodes et les variables locales non publiques de la classe / méthode environnante qui, à l'extérieur (si une classe distincte devait être utilisée) devrait être adopté ou publié.
icza

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

C'est également l'un des exemples de type interne anonyme utilisant un thread


3

j'utilise des objets anonymes pour appeler de nouveaux Threads ..

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Une classe interne est associée à une instance de la classe externe et il existe deux types spéciaux: classe locale et classe anonyme . Une classe anonyme nous permet de déclarer et d'instancier une classe en même temps, ce qui rend le code concis. Nous les utilisons lorsque nous avons besoin d'une classe locale une seule fois car ils n'ont pas de nom.

Prenons l'exemple de doc où nous avons une Personclasse:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

et nous avons une méthode pour imprimer les membres qui correspondent aux critères de recherche comme:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

CheckPersonest une interface comme:

interface CheckPerson {
    boolean test(Person p);
}

Nous pouvons maintenant utiliser une classe anonyme qui implémente cette interface pour spécifier des critères de recherche comme:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Ici, l'interface est très simple et la syntaxe de la classe anonyme semble compliquée et peu claire.

Java 8 a introduit un terme interface fonctionnelle qui est une interface avec une seule méthode abstraite, donc nous pouvons dire que CheckPersonc'est une interface fonctionnelle. Nous pouvons utiliser l' expression Lambda qui nous permet de passer la fonction comme argument de méthode comme:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Nous pouvons utiliser une interface fonctionnelle standard Predicateà la place de l'interface CheckPerson, ce qui réduira encore la quantité de code requise.


2

La classe interne anonyme peut être bénéfique tout en donnant différentes implémentations pour différents objets. Mais doit être utilisé avec parcimonie car cela crée un problème de lisibilité du programme.


1

L'une des principales utilisations des classes anonymes dans la finalisation des classes qui appelait le gardien du finaliseur . Dans le monde Java, l'utilisation des méthodes de finalisation doit être évitée jusqu'à ce que vous en ayez vraiment besoin. Vous devez vous rappeler que lorsque vous remplacez la méthode finalize pour les sous-classes, vous devez toujours invoquer super.finalize()également, car la méthode finalize de super class ne sera pas invoquée automatiquement et vous pouvez avoir des problèmes avec les fuites de mémoire.

compte tenu du fait mentionné ci-dessus, vous pouvez simplement utiliser les classes anonymes comme:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

En utilisant cette technique, vous avez soulagé vous-même et vos autres développeurs d'appeler super.finalize()chaque sous-classe de la HeavyClassméthode qui doit être finalisée.


1

Vous pouvez utiliser la classe anonyme de cette façon

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Il semble que personne ne soit mentionné ici, mais vous pouvez également utiliser une classe anonyme pour contenir un argument de type générique (qui a normalement été perdu en raison de l'effacement de type) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Si vous instanciez cette classe de manière anonyme

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

alors cette holderinstance contiendra une définition non effacée du type passé.

Usage

C'est très pratique pour construire des validateurs / désérialisateurs. Vous pouvez également instancier un type générique avec réflexion (donc si vous avez toujours voulu le faire new T()en type paramétré - vous êtes les bienvenus!) .

Inconvénients / limitations

  1. Vous devez transmettre explicitement le paramètre générique. Ne pas le faire entraînera une perte de paramètres de type
  2. Chaque instanciation vous coûtera une classe supplémentaire à générer par le compilateur, ce qui entraîne une pollution du chemin de classe / des ballonnements

1

La meilleure façon d'optimiser le code. aussi, nous pouvons utiliser pour une méthode prioritaire d'une classe ou d'une interface.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Une classe interne anonyme est utilisée pour créer un objet qui ne sera plus jamais référencé. Il n'a pas de nom et est déclaré et créé dans la même instruction. Ceci est utilisé là où vous utiliseriez normalement la variable d'un objet. Vous remplacez la variable par le newmot clé, un appel à un constructeur et la définition de classe à l'intérieur de {et }.

Lors de l'écriture d'un programme threadé en Java, il ressemblerait généralement à ceci

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

L' ThreadClassutilisé ici serait défini par l'utilisateur. Cette classe implémentera l' Runnableinterface requise pour créer des threads. Dans la ThreadClassla run()méthode (seule méthode Runnable) doit être mis en œuvre aussi bien. Il est clair que se débarrasser de ThreadClassserait plus efficace et c'est exactement pourquoi il existe des classes internes anonymes.

Regardez le code suivant

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Ce code remplace la référence faite taskdans l'exemple le plus en haut. Plutôt que d'avoir une classe distincte, la classe interne anonyme à l'intérieur du Thread()constructeur renvoie un objet sans nom qui implémente l' Runnableinterface et remplace la run()méthode. La méthode run()comprendrait des instructions à l'intérieur qui effectuent le travail requis par le thread.

Répondant à la question de savoir si les classes internes anonymes est l'un des avantages de Java, je dois dire que je ne suis pas sûr car je ne connais pas beaucoup de langages de programmation pour le moment. Mais ce que je peux dire, c'est que c'est définitivement une méthode de codage plus rapide et plus facile.

Références: Sams Teach Yourself Java in 21 Days Seventh Edition


0

Un autre avantage:
comme vous savez que Java ne prend pas en charge l'héritage multiple, donc si vous utilisez une sorte de classe "Thread" comme classe anonyme, la classe a encore un espace pour toute autre classe à étendre.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.