Qu'est-ce que l'initialisation à double Accolade en Java?

Qu'est-ce que la syntaxe d'initialisation à double Accolade ({{ ... }}) en Java?

215
demandé sur Andrew Tobilko 2009-12-24 18:06:45

11 réponses

L'initialisation à double Accolade crée une classe anonyme dérivée de la classe spécifiée (les accoladesexternes ) et fournit un bloc initialiseur au sein de cette classe (les accolades internes). par exemple

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

Notez qu'un effet de l'utilisation de cette initialisation à double Accolade est que vous créez des classes internes anonymes. La classe créée a un pointeur this implicite sur la classe externe environnante. Bien que ce ne soit normalement pas un problème, il peut causer du chagrin dans certaines circonstances, par exemple lors de la sérialisation ou de la collecte des ordures, et cela vaut la peine d'être conscient de cela.

215
répondu Brian Agnew 2015-06-02 09:07:45

Chaque fois que quelqu'un utilise l'initialisation de double Accolade, un chaton est tué.

Outre que la syntaxe est plutôt inhabituelle et pas vraiment idiomatique (le goût est discutable, bien sûr), vous créez inutilement deux problèmes importants dans votre application, dont j'ai récemment parlé plus en détail ici.

1. Vous créez beaucoup trop de classes anonymes

Chaque fois que vous utilisez l'initialisation à double Accolade, une nouvelle classe est créée. E. g. cet exemple:

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... produira ces classes:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

C'est un peu de frais généraux pour votre classloader-pour rien! Bien sûr, cela ne prendra pas beaucoup de temps d'initialisation si vous le faites une fois. Mais si vous faites cela 20 ' 000 fois tout au long de votre application d'entreprise... tout ce tas de mémoire juste pour un peu de "sucre de syntaxe"?

2. Vous créez potentiellement une fuite de mémoire!

Si vous prenez le code ci-dessus et renvoyez cette carte à partir d'une méthode, les appelants de cette la méthode pourrait être sans méfiance tenant à des ressources très lourdes qui ne peuvent pas être collectées. Prenons l'exemple suivant:

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

Retournés Map contient maintenant une référence à l'instance englobante de ReallyHeavyObject. Vous ne voulez probablement pas risquer cela:

Fuite De Mémoire Ici

Image de http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/

3. Vous pouvez prétendre que Java a des littéraux de carte

À répondez à votre question réelle, les gens ont utilisé cette syntaxe pour prétendre que Java a quelque chose comme des littéraux de carte, similaires aux littéraux de tableau existants:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

Certaines personnes peuvent trouver cette stimulation syntaxique.

219
répondu Lukas Eder 2014-12-17 08:53:38
  • La première Accolade crée une nouvelle classe interne anonyme.
  • le deuxième ensemble d'accolades crée un initialiseur d'instance comme un bloc statique en classe.

Par exemple:

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set<String> keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

Comment cela fonctionne

First brace crée une nouvelle classe interne anonyme. Ces classes internes sont capables d'accéder au comportement de leur classe parent. Donc, dans notre cas, nous créons en fait une sous-classe de la classe HashSet, donc cette classe interne est capables d'utiliser la méthode add ().

Et le deuxième ensemble d'accolades ne sont que des initialiseurs d'instance. Si vous rappelez des concepts java de base, vous pouvez facilement associer des blocs d'initialisation d'instance à des initialiseurs statiques en raison d'une accolade similaire à la structure. La seule différence est que static initializer est ajouté avec le mot-clé static, et n'est exécuté qu'une seule fois; peu importe le nombre d'objets que vous créez.

Plus de

35
répondu Premraj 2018-04-17 13:01:57

Pour une application amusante d'initialisation à double Accolade, voir ici le tableau de Dwemthy en Java .

Un extrait

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

Et maintenant, préparez - vous pour le BattleOfGrottoOfSausageSmells et ... chunky bacon!

21
répondu akuhn 2009-12-24 16:57:24

Je pense qu'il est important de souligner que Il n'y a pas d ' "initialisation à double Accolade" en Java. Le site Web Oracle n'a pas ce terme. Dans cet exemple, il y a deux fonctionnalités utilisées ensemble: classe anonyme et bloc initialiseur. On dirait que l'ancien bloc d'initialisation a été oublié par les développeurs et causer une certaine confusion dans ce sujet. Citation de Oracle docs:

Les blocs D'initialisation par exemple les variables ressemblent à des blocs d'initialisation statiques, mais sans le mot-clé statique:

{
    // whatever code is needed for initialization goes here
}
9
répondu Alex T 2016-12-20 13:18:41

Je voudrais souligner qu'il n'y a pas d'initialisation à double Accolade. Il n'y a que le bloc d'initializaition traditionnel normal d'une accolade. Le deuxième bloc d'accolades n'a rien à voir avec l'initialisation. Les réponses disent que ces deux accolades initialisent quelque chose, mais ce n'est pas comme ça.

Deuxièmement, presque toutes les réponses disent que c'est une chose utilisée lors de la création de classes internes anonymes. Je pense que les gens qui lisent ces réponses auront l'impression que cela n'est utilisé que lorsque création de classes internes anonymes. Mais il est utilisé dans toutes les classes. La lecture de ces réponses ressemble à un nouvel avenir spécial dédié aux classes anonymes et je pense que c'est trompeur.

Pour aller plus loin, cette question parle de la situation où le deuxième support d'ouverture est juste après le premier support d'ouverture. Lorsqu'il est utilisé en classe normale, il y a généralement du code entre deux accolades, mais c'est totalement la même chose. Il s'agit donc de placer des parenthèses. Donc je pense que nous ne devrions pas dire que c'est une nouvelle chose passionnante, car c'est la chose que nous connaissons tous, mais juste écrite avec du code entre parenthèses. Nous ne devrions pas créer un nouveau concept appelé "initialisation à double Accolade".

Je ne suis pas d'accord avec un argument selon lequel vous créez trop de classes anonymes. Vous ne les créez pas parce qu'un bloc d'initialisation, mais simplement parce que vous les Créez. Ils seraient créés même si vous n'utilisiez pas l'initialisation de deux accolades de sorte que ces problèmes se produiraient même sans initialisation... L'initialisation n'est pas le facteur qui crée l'objet initialisé.

De plus, nous ne devrions pas parler de problème créé en utilisant cette chose inexistante "initialisation à double Accolade" ou même par une initialisation normale à un support, car les problèmes décrits n'existent qu'à cause de la création d'une classe anonyme, donc cela n'a rien à voir avec la question originale. Mais toutes les réponses avec donnent aux lecteurs l'impression que ce n'est pas la faute de créer des classes anonymes, mais ce mal chose (inexistante) appelée "initialisation à double Accolade".

7
répondu ctomek 2015-11-28 19:11:41

Pour éviter tous les effets négatifs de l'initialisation à double Accolade, tels que:

  1. compatibilité "égale" brisée.
  2. Aucune vérification effectuée, lorsque vous utilisez des affectations directes.
  3. fuites de mémoire possibles.

Faites les choses suivantes:

  1. créez une classe "Builder" séparée en particulier pour l'initialisation à double Accolade.
  2. déclare les champs avec des valeurs par défaut.
  3. mettre la méthode de création d'objet dans ce classe.

Exemple:

public class MyClass {
    public static class Builder {
        public int    first  = -1        ;
        public double second = Double.NaN;
        public String third  = null      ;

        public MyClass create() {
            return new MyClass(first, second, third);
        }
    }

    protected final int    first ;
    protected final double second;
    protected final String third ;

    protected MyClass(
        int    first ,
        double second,
        String third
    ) {
        this.first = first ;
        this.second= second;
        this.third = third ;
    }

    public int    first () { return first ; }
    public double second() { return second; }
    public String third () { return third ; }
}

Utilisation:

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

Avantages:

  1. simplement à utiliser.
  2. ne casse pas la compatibilité "égale".
  3. vous pouvez effectuer des vérifications dans la méthode de création.
  4. Pas de fuites de mémoire.

Inconvénients:

  • néant.

Et, par conséquent, nous avons le modèle java builder le plus simple jamais.

Voir tous les échantillons sur github: java-sf-builder-simple-exemple

4
répondu Boris Podchezertsev 2015-09-04 18:19:03

C'est - entre autres utilisations-un raccourci pour initialiser les collections. en savoir plus ...

3
répondu miku 2009-12-24 15:14:11

Vous voulez dire quelque chose comme ça?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

C'est une initialisation de liste de tableau en temps de création (hack)

3
répondu dhblah 2011-03-29 16:15:19

Vous pouvez mettre quelques instructions Java en boucle pour initialiser la collection:

List<Character> characters = new ArrayList<Character>() {
    {
        for (char c = 'A'; c <= 'E'; c++) add(c);
    }
};

Random rnd = new Random();

List<Integer> integers = new ArrayList<Integer>() {
    {
         while (size() < 10) add(rnd.nextInt(1_000_000));
    }
};

Mais ce cas affecte les performances, vérifiez ceci discussion

3
répondu Anton Dozortsev 2017-05-23 12:10:10

Cela semble être le même que le mot-clé with si populaire dans flash et vbscript. C'est une méthode pour changer ce qu'est this et rien de plus.

0
répondu Chuck Vose 2009-12-24 16:56:29