web-dev-qa-db-fra.com

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

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

284
Warrior

Par "classe anonyme", je suppose que vous voulez dire 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 "suppléments" 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 accélère un peu le codage, 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 créer une classe séparée.

Je n'utilise cette technique que pour des tâches "rapides et sales" où il est inutile de faire toute une classe. Avoir plusieurs classes internes anonymes qui font exactement la même chose devrait être refactored en une classe réelle, que ce soit une classe interne ou une classe séparée.

348
coobird

Les classes internes anonymes sont effectivement 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 anonymement pour créer une fonction de première classe en Java. Supposons que la méthode suivante retourne le premier nombre plus grand que 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 inférieur à 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 F de première classe, 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);

Ceci est un exemple vraiment artificiel, mais il est facile de voir que pouvoir transmettre des fonctions comme si elles étaient des valeurs est une fonctionnalité très utile. Voir "Votre langage de programmation peut-il faire cela" par Joel lui-même.

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

70
Apocalisp

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

1.) Pour la dérogation (sous-classification), lorsque la définition de classe n'est pas utilisable, sauf 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 implémenter une interface, Lorsque l'implémentation d'une interface est requise uniquement dans le cas présent:

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 l'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");
       } 
     });
   } 
 } 
47
Sandeep Kumar

Je les utilise parfois comme un hack de syntaxe pour l'instanciation de Map:

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

contre

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

Cela évite une certaine redondance lorsque vous faites beaucoup de déclarations de vente. Cependant, j'ai également eu du mal à le faire lorsque la classe externe doit être sérialisée via Remoting. 

44
Chase Seibert

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 à devoir créer une classe nommée à chaque fois, mais des concepts similaires sont beaucoup mieux implémentés dans d'autres langages (comme des fermetures ou des blocs)

Voici un exemple de swing

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

Bien que ce soit toujours bavard, il vaut beaucoup mieux que de vous obliger à définir une classe nommée pour chaque auditeur jeté comme celui-ci (bien que, en fonction de la situation et de la réutilisation, cette approche puisse rester la meilleure)

17
madlep

Vous l'utilisez dans des situations où vous devez créer une classe dans un autre but, 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 de l'intérieur du code d'une fonction afin de ne jamais y faire référence ailleurs, vous n'avez donc pas besoin de les nommer. Le compilateur les énumère simplement.

Il s’agit essentiellement de sucre syntaxique et devrait généralement être transféré ailleurs à mesure qu’ils grossissent.

Je ne suis pas sûr que ce soit l'un des avantages de Java, bien que si vous les utilisez (et nous les utilisons tous fréquemment, malheureusement), vous pourriez alors soutenir qu'ils le sont.

7
Uri

Lignes de guide pour la classe anonyme.

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

  2. La classe anonyme doit être étendue ou implémentée à une et une seule classe ou interface, respectivement.

  3. Comme la classe anonyme 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

    }
});
6
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 thread

5
raja

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

Avec une classe interne anonyme, vous avez accès aux variables final et membre 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 (du 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).

4
Lars A Frøyland

j'utilise des objets anonymes pour appeler de nouvelles discussions.

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

Une classe intérieure est associée à une instance de la classe extérieure 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 classe Person:

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

CheckPerson est une interface du type:

interface CheckPerson {
    boolean test(Person p);
}

Nous pouvons maintenant utiliser la classe anonyme qui implémente cette interface pour spécifier les 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 difficile à manier et peu claire.

Java 8 a introduit un terme Interface fonctionnelle qui est une interface avec une seule méthode abstraite. On peut donc dire que CheckPerson est une interface fonctionnelle. Nous pouvons utiliser 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
i_am_zero

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.

2
user1923551

Une des principales utilisations des classes anonymes dans la finalisation de classe est appelée finalizer guardian . Dans le monde Java, l'utilisation des méthodes de finalisation doit être évitée jusqu'à ce que vous en ayez réellement besoin. Vous devez vous rappeler que, lorsque vous substituez la méthode finalize aux sous-classes, vous devez toujours appeler super.finalize() également, car la méthode finalize de la super classe n'appelle pas automatiquement et vous pouvez avoir des problèmes de fuites de mémoire.

donc, 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 et vos autres développeurs avez décidé d'appeler super.finalize() sur chaque sous-classe de la HeavyClass qui nécessite la méthode de finalisation.

1
Hazhir

Personne ne semble mentionné ici, mais vous pouvez également utiliser une classe anonyme pour contenir l'argument de type générique (normalement 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 instance holder contiendra une définition non effacée du type passé. 

Usage

Ceci est très pratique pour la construction de validateurs/désérialisateurs. Vous pouvez aussi instancier le type générique avec réflexion (donc si vous avez toujours voulu faire new T() en type paramétré - vous êtes le bienvenu!) .

Inconvénients/Limitations

  1. Vous devez transmettre explicitement le paramètre générique. Sinon, cela entraînera une perte de paramètre de type
  2. Chaque instanciation vous coûtera une classe supplémentaire à générer par le compilateur, ce qui entraînera une pollution par chemin de classe/ballonnement de jar
1
vsminkov

Vous pouvez utiliser une classe anonyme de cette façon

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

An Anonymous Inner Class est utilisé 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 mot clé new, 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();

La ThreadClass utilisée ici serait définie par l'utilisateur. Cette classe implémentera l'interface Runnable qui est requise pour créer des threads. Dans ThreadClass, la méthode run() (uniquement dans Runnable) doit également être implémentée. Il est clair que se débarrasser de ThreadClass serait plus efficace, et c’est exactement pourquoi les classes internes anonymes existent.

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 à task dans le plus haut exemple. Plutôt que d'avoir une classe séparée, la classe interne anonyme du constructeur Thread() renvoie un objet non nommé qui implémente l'interface Runnable et substitue la méthode run(). La méthode run() inclurait des instructions à l'intérieur qui font le travail requis par le thread.

Pour répondre à la question de savoir si les classes internes anonymes sont l'un des avantages de Java, je dois dire que je ne suis pas tout à fait sûr, car je ne connais pas encore beaucoup de langages de programmation. Mais ce que je peux dire, c’est vraiment une méthode de codage plus rapide et plus simple.

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

0
Avishka Ariyaratne

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

}
0
Shivendra Pandey

Un autre avantage:
Comme vous savez que Java ne prend pas en charge l'héritage multiple, si vous utilisez une classe "Thread" comme classe anonyme, la classe a toujours un espace disponible pour être étendue par toute autre classe.

0
Arun Raaj