Qu'est-ce que la syntaxe d'initialisation Double Brace ({{ ... }}
) en Java?
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.
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 .
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"?
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:
Image de http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/
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.
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.
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!
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
}
Pour éviter tous les effets négatifs de l’initialisation à double attache, tels que:
faire les choses suivantes:
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:
Désavantages:
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
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".
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)
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));
}
};
C'est, entre autres utilisations, un raccourci pour initialiser les collections. Apprendre encore plus ...
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 .
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.
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.