71 votes

Blocs de code anonymes en Java

Existe-t-il des utilisations pratiques des blocs de code anonymes en Java ?

public static void main(String[] args) {
    // in
    {
        // out
    }
}

Veuillez noter qu'il ne s'agit pas de nommé blocs, c'est-à-dire

name: { 
     if ( /* something */ ) 
         break name;
}

.

8 votes

Eh bien, j'ai tendance à les utiliser en conjonction avec if , else , for , while , do , switch et case .

5 votes

Par ailleurs, moi aussi, mais j'ai décidé de ne pas exclure les utilisations qui me semblaient évidentes, comme celles que vous avez mentionnées.

137voto

David Seiler Points 6212

Ils limitent la portée des variables.

public void foo()
{
    {
        int i = 10;
    }
    System.out.println(i); // Won't compile.
}

Dans la pratique, cependant, si vous vous retrouvez à utiliser un tel bloc de code, c'est probablement un signe que vous devez refactoriser ce bloc en méthode.

3 votes

Bonne réponse, et tout à fait d'accord avec le commentaire sur la méthode.

8 votes

+1 : bonne réponse, et c'est le signe d'une refactorisation désespérément nécessaire, ou d'une "odeur de code" comme le dit Martin Fowler :)

3 votes

La seule fois où vous ne voulez pas le factoriser dans une méthode privée est si ce bloc de code a besoin de beaucoup de contexte (sous la forme d'autres variables locales). Mais c'est probablement une autre odeur de code...

48voto

Stephen Swensen Points 13439

La réponse de @David Seiler est correcte, mais je soutiens que les blocs de code sont très utiles et devraient être utilisés fréquemment, et qu'ils n'indiquent pas nécessairement le besoin de factoriser dans une méthode. Je trouve qu'ils sont particulièrement utiles pour construire des arbres de composants Swing, par ex :

JPanel mainPanel = new JPanel(new BorderLayout());
{
    JLabel centerLabel = new JLabel();
    centerLabel.setText("Hello World");
    mainPanel.add(centerLabel, BorderLayout.CENTER);
}
{
    JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0,0));
    {
        JLabel label1 = new JLabel();
        label1.setText("Hello");
        southPanel.add(label1);
    }
    {
        JLabel label2 = new JLabel();
        label2.setText("World");
        southPanel.add(label2);
    }
    mainPanel.add(southPanel, BorderLayout.SOUTH);
}

Non seulement les blocs de code limitent la portée des variables aussi étroitement que possible (ce qui est toujours bon, surtout lorsqu'il s'agit d'états mutables et de variables non finales), mais ils illustrent également la hiérarchie des composants à la manière du XML / HTML, ce qui rend le code plus facile à lire, à écrire et à maintenir.

Mon problème avec la factorisation de chaque instanciation de composant dans une méthode est que

  1. La méthode ne sera utilisée qu'une seule fois et pourtant exposée à un public plus large, même s'il s'agit d'une méthode d'instance privée.
  2. C'est plus difficile à lire, en imaginant un arbre de composants plus profond et plus complexe, il faudrait creuser pour trouver le code qui vous intéresse, et ensuite perdre le contexte visuel.

Dans cet exemple de Swing, je trouve que lorsque la complexité devient vraiment trop importante pour être gérable, cela indique qu'il est temps de factoriser une branche de l'arbre dans une nouvelle classe plutôt que dans une série de petites méthodes.

12 votes

Je suis en train de travailler sur du code qui utilise beaucoup cette "fonctionnalité" et c'est plutôt difficile à suivre. Je pense que c'est une mauvaise façon de structurer le code.

2 votes

Jan - il y a beaucoup de façons de mal utiliser de bonnes fonctionnalités, et je peux imaginer que celle-ci devienne assez facilement incontrôlable (imbrication profonde, long défilement, beaucoup de mutation, ...).

1 votes

@Steven - Je n'ai pas encore vu un seul exemple où cette fonctionnalité ne mène pas à un fouillis incompréhensible. Votre exemple ci-dessus est correct, mais plutôt petit.

25voto

Kevin K Points 4279

Il est généralement préférable de faire en sorte que la portée des variables locales soit la plus petite possible . Les blocs de code anonymes peuvent y contribuer.

Je trouve cela particulièrement utile pour switch déclarations. Considérez l'exemple suivant, sans blocs de code anonymes :

public String manipulate(Mode mode) {
    switch(mode) {
    case FOO: 
        String result = foo();
        tweak(result);
        return result;
    case BAR: 
        String result = bar();  // Compiler error
        twiddle(result);
        return result;
    case BAZ: 
        String rsult = bar();   // Whoops, typo!
        twang(result);  // No compiler error
        return result;
    }
}

Et avec des blocs de code anonymes :

public String manipulate(Mode mode) {
    switch(mode) {
        case FOO: {
            String result = foo();
            tweak(result);
            return result;
        }
        case BAR: {
            String result = bar();  // No compiler error
            twiddle(result);
            return result;
        }
        case BAZ: {
            String rsult = bar();   // Whoops, typo!
            twang(result);  // Compiler error
            return result;
        }
    }
}

Je considère que la deuxième version est plus propre et plus facile à lire. De plus, elle réduit la portée des variables déclarées dans le switch au cas pour lequel elles ont été déclarées, ce qui, d'après mon expérience, est ce que vous voulez 99% du temps de toute façon.

Attention cependant, il ne no changer le comportement pour les cas d'échec - vous devrez toujours vous rappeler d'inclure une clause d'annulation de cas. break o return pour l'empêcher !

20voto

Stephen C Points 255558

Je pense que vous et/ou les autres réponses confondent deux constructions syntaxiques distinctes, à savoir les initialisateurs d'instance et les blocs. (Et d'ailleurs, un "bloc nommé" est en réalité un Labeled Statement, où le Statement se trouve être un Block).

Un initialisateur d'instance est utilisé au niveau syntaxique d'un membre de classe ; par exemple

public class Test {
    final int foo;

    {
         // Some complicated initialization sequence; e.g.
         int tmp;
         if (...) {
             ...
             tmp = ...
         } else {
             ...
             tmp = ...
         }
         foo = tmp;
    }
}

La construction Initializer est le plus souvent utilisée avec des classes anonymes comme dans l'exemple de @dfa. Un autre cas d'utilisation est l'initialisation compliquée d'attributs "finaux", comme dans l'exemple ci-dessus. (Cependant, il est plus courant de faire cela en utilisant un constructeur régulier. Le modèle ci-dessus est plus couramment utilisé avec les initialisateurs statiques).

L'autre construction est un bloc ordinaire et apparaît à l'intérieur d'un bloc de code tel que la méthode ; par ex.

public void test() {
    int i = 1;
    {
       int j = 2;
       ...
    }
    {
       int j = 3;
       ...
    }
}

Les blocs sont le plus souvent utilisés dans le cadre d'instructions de contrôle pour regrouper une séquence d'instructions. Mais lorsque vous les utilisez au-dessus, ils vous permettent (simplement) de restreindre la visibilité des déclarations ; par ex. j dans ce qui précède.

Cela indique généralement que vous devez remanier votre code, mais ce n'est pas toujours évident. Par exemple, on voit parfois ce genre de choses dans les interprètes codés en Java. Les instructions dans les bras de commutation pourraient être intégrées dans des méthodes distinctes, mais cela peut entraîner une baisse significative des performances de la "boucle interne" d'un interpréteur, par exemple

    switch (op) {
    case OP1: {
             int tmp = ...;
             // do something
             break;
         }
    case OP2: {
             int tmp = ...;
             // do something else
             break;
         }
    ...
    };

1 votes

Merci ! Pourriez-vous nous en dire plus ou nous indiquer un article sur la partie "perte de performance pour la boucle interne de l'interpréteur" ?

0 votes

L'appel d'une méthode (qui ne peut pas être inlined) prend des cycles CPU supplémentaires - c'est une perte de performance. Cela peut être significatif

13voto

OscarRyz Points 82553

Vous pouvez l'utiliser comme constructeur pour des classes internes anonymes.

Comme ça :

alt text

De cette façon, vous pouvez initialiser votre objet, puisque le bloc libre est exécuté pendant la construction de l'objet.

Elle n'est pas limitée aux classes internes anonymes, elle s'applique également aux classes ordinaires.

public class SomeClass {
    public List data;{
        data = new ArrayList();
        data.add(1);
        data.add(1);
        data.add(1);
    }
}

7 votes

C'est en fait un initialisateur d'instance, ce qui est une chose différente.

2 votes

Différent de... ? Les constructeurs ? Ah oui, je veux dire "en tant que substitut ou complément de..." en parlant de constructeur.

1 votes

Différent pour coder des blocs à l'intérieur de méthodes, comme le montre l'exemple de code de la question.

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