web-dev-qa-db-fra.com

Qu'est-ce que l'initialisation Double Brace en Java?

Qu'est-ce que la syntaxe d'initialisation Double Brace ({{ ... }}) en Java?

240
sgokhales

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

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

Notez que l’utilisation de cette initialisation à double accolade a pour effet de créer des classes internes anonymes. La classe créée a un pointeur implicite this vers 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 mise en série ou de la collecte des ordures, il convient de le savoir.

241
Brian Agnew

Chaque fois que quelqu'un utilise l'initialisation à double attelle, 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, que j'ai récemment commenté plus en détail ici .

1. Vous créez trop de classes anonymes

Chaque fois que vous utilisez l'initialisation à double accolade, une nouvelle classe est créée. Par exemple. 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 cours:

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

C'est pas mal de frais généraux pour votre chargeur de classe - 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 dans votre application d'entreprise ... toute cette mémoire mémoire pour un peu de "sucre 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 méthode risquent fort de garder des ressources très lourdes qui ne peuvent pas être récupéré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;
    }
}

La Map renvoyée contiendra désormais une référence à l'instance englobante de ReallyHeavyObject. Vous ne voulez probablement pas risquer que:

Memory Leak Right Here

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

Pour répondre à votre question, les gens ont utilisé cette syntaxe pour prétendre que Java a quelque chose de similaire aux 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 cela stimulant syntaxiquement.

239
Lukas Eder
  • 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 dans Class. 

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 ça marche 

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

Et Second ensemble d'accolades ne sont que des initialiseurs d'instance. Si vous rappelez les concepts Java de base, vous pouvez facilement associer des blocs d’initialisation d’instance à des initialiseurs statiques grâce à un support similaire à struct. La seule différence est que l'initialiseur statique 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

35
Premraj

Pour une application amusante d’initialisation à double accolade, voir ici Dwemthy’s Array 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 à la BattleOfGrottoOfSausageSmells et au… gros bacon!

21
akuhn

Je pense qu’il est important de souligner que il n’existe pas d’initialisation "Double Brace" en Java. Le site Web Oracle n'a pas ce terme. Dans cet exemple, deux fonctionnalités sont utilisées ensemble: une classe anonyme et un bloc d’initialisation. On dirait que l'ancien bloc d'initialisation a été oublié par les développeurs et crée une certaine confusion dans cette rubrique. Citation de documents Oracle :

Les blocs d'initialisation pour les variables d'instance ressemblent aux blocs d'initialisation statiques, mais sans le mot clé static:

{
    // whatever code is needed for initialization goes here
}
11
Alex T

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

  1. Cassé "égal" compatibilité.
  2. Aucune vérification effectuée lorsque vous utilisez des affectations directes. 
  3. Fuites de mémoire possibles.

faire les choses suivantes:

  1. Créez une classe "Builder" distincte, en particulier pour l'initialisation à double accolade.
  2. Déclarez les champs avec les valeurs par défaut.
  3. Mettez la méthode de création d'objet dans cette 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 ; }
}

Usage:

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. Aucune fuite de mémoire.

Désavantages:

  • Aucun.

En conséquence, nous avons le modèle de constructeur Java le plus simple jamais créé.

Voir tous les exemples sur github: Java-sf-builder-simple-example

7

1- Il n'y a pas de doubles accolades:
J'aimerais souligner qu’il n’existe pas d’initialisation à double accolade. Il n’existe qu’un bloc d’initialisation traditionnel normal. 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.

2- Il ne s'agit pas seulement de classes anonymes mais de toutes les classes:
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 lecteurs de ces réponses auront l’impression que cela n’est utilisé que lors de la création de classes internes anonymes. Mais il est utilisé dans toutes les classes. La lecture de ces réponses est une toute nouvelle fonctionnalité dédiée aux classes anonymes et je pense que cela est trompeur.

3- Le but est juste de placer les crochets les uns après les autres, pas un nouveau concept:
Pour aller plus loin, cette question parle de la situation lorsque la deuxième parenthèse ouvrante se situe juste après la première parenthèse ouvrante. Lorsqu'il est utilisé en classe normale, il y a généralement un code entre deux accolades, mais c'est totalement la même chose. Il s’agit donc de placer des crochets. Donc, je pense que nous ne devrions pas dire que ceci est une nouvelle chose excitante, parce que c'est la chose que nous connaissons tous, mais juste écrit avec un code entre parenthèses. Nous ne devrions pas créer un nouveau concept appelé "initialisation à double accolade".

4- La création de classes anonymes imbriquées n'a rien à voir avec deux accolades:
Je ne suis pas d'accord avec un argument selon lequel vous créez trop de classes anonymes. Vous ne les créez pas à cause d'un bloc d'initialisation, mais simplement parce que vous les créez. Ils seraient créés même si vous n'aviez pas utilisé l'initialisation à deux accolades pour que ces problèmes se produisent même sans initialisation ... L'initialisation n'est pas le facteur qui crée les objets initialisés.

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 entre crochets, car les problèmes décrits n’existent que du fait de la création d’une classe anonyme et n’ont donc rien à voir avec la question originale. Mais toutes les réponses donnent aux lecteurs l’impression que ce n’est pas une faute de créer des classes anonymes, mais cette chose diabolique (inexistante) appelée "initialisation à double attache".

6
ctomek

tu veux dire quelque chose comme ca?

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

c'est une initialisation de liste de tableaux au moment de la création (hack)

3
dhblah

Vous pouvez mettre certaines 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 cette affaire affecte la performance, vérifiez cette discussion

3
Anton Dozortsev

C'est, entre autres utilisations, un raccourci pour initialiser les collections. Apprendre encore plus ...

3
miku

L'initialisation à double accolade tire parti de la syntaxe de la classe interne. Supposons que vous vouliez construire une liste de tableaux et la transmettre à une méthode:

ArrayList<String> friends = new ArrayList<>();
friends.add("Mark");
friends.add("Steve");
invite(friends);

Si vous n'avez plus besoin de la liste des tableaux, il serait bien de la rendre anonyme. Mais alors comment pouvez-vous ajouter les éléments? (l'initialisation des doubles accouplements vient ici) Voici comment:

invite(new ArrayList<String>({{ add("Mark"); add("Steve");}});

Notez les doubles accolades. Les accolades externes forment une sous-classe anonyme de ArrayList. Les accolades intérieures sont un bloc de construction objet .

2
hamza belmellouki

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

0
Chuck Vose

Comme le souligne @Lukas Eder il faut éviter l'initialisation des collections par double accolade.  

Il crée une classe interne anonyme et, comme toutes les classes internes conservent une référence à l'instance parente, il peut empêcher - 99% - d'empêcher la collecte de déchets si ces objets de collection sont référencés par plus d'objets que le seul déclarant.

Java 9 a introduit les méthodes de commodité List.of, Set.of et Map.of, qui doivent être utilisées à la place. Ils sont plus rapides et plus efficaces que l'initialiseur à double accolade.

0
Mikhail Kholodkov