Cos'è l'inizializzazione della doppia parentesi in Java?


Cos'è la sintassi di inizializzazione della doppia parentesi graffa ({{ ... }}) in Java?

Author: Andrew Tobilko, 2009-12-24

11 answers

L'inizializzazione della doppia parentesi graffa crea una classe anonima derivata dalla classe specificata (le parentesi esterne ) e fornisce un blocco inizializzatore all'interno di tale classe (le parentesi interne ). ad esempio

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

Nota che un effetto dell'utilizzo di questa inizializzazione a doppia parentesi è che stai creando classi interne anonime. La classe creata ha un puntatore implicito this alla classe esterna circostante. Anche se normalmente non è un problema, può causare dolore in alcune circostanze, ad esempio quando serializzazione o raccolta dei rifiuti, e vale la pena essere consapevoli di questo.

 215
Author: Brian Agnew, 2015-06-02 09:07:45

Ogni volta che qualcuno usa l'inizializzazione del doppio tutore, un gattino viene ucciso.

A parte che la sintassi è piuttosto insolita e non proprio idiomatica (il gusto è discutibile, ovviamente), stai creando inutilmente due problemi significativi nella tua applicazione, di cui ho recentemente scritto più dettagliatamente qui .

1. Stai creando troppe classi anonime

Ogni volta che si utilizza l'inizializzazione doppia parentesi viene creata una nuova classe. Ad esempio questo esempio:

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

... produrrà queste classi:

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

Questo è un bel po ' di overhead per il tuo classloader - per niente! Ovviamente non ci vorrà molto tempo di inizializzazione se lo fai una volta. Ma se lo fai 20 ' 000 volte in tutta l'applicazione aziendale... tutta quella memoria di heap solo per un po 'di"zucchero di sintassi"?

2. Stai potenzialmente creando una perdita di memoria!

Se prendi il codice sopra e restituisci quella mappa da un metodo, i chiamanti di quello il metodo potrebbe essere inconsapevolmente aggrappato a risorse molto pesanti che non possono essere raccolte. Si consideri il seguente esempio:

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

Il Map restituito ora conterrà un riferimento all'istanza che racchiude ReallyHeavyObject. Probabilmente non vuoi rischiare che:

Perdita di memoria proprio qui

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

3. Puoi fingere che Java abbia i letterali della mappa

Da

A rispondi alla tua vera domanda, le persone hanno usato questa sintassi per fingere che Java abbia qualcosa come i letterali della mappa, simili ai letterali dell'array esistente:

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

Alcune persone possono trovare questo sintatticamente stimolante.

 219
Author: Lukas Eder, 2014-12-17 08:53:38
  • La prima parentesi crea una nuova classe interna anonima.
  • Il secondo set di parentesi graffa crea un'istanza inizializzatori come blocco statico in Classe.

Ad esempio:

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

}

Come funziona

First brace crea una nuova classe interna anonima. Queste classi interne sono in grado di accedere al comportamento della loro classe genitore. Quindi, nel nostro caso, stiamo effettivamente creando una sottoclasse di classe HashSet, quindi questa classe interna è in grado di utilizzare il metodo add ().

E Secondo set di parentesi graffe non sono altro che inizializzatori di istanze. Se si ricordano i concetti java di base, è possibile associare facilmente i blocchi di inizializzazione delle istanze con inizializzatori statici a causa di una parentesi graffa simile a struct. L'unica differenza è che l'inizializzatore statico viene aggiunto con la parola chiave statica e viene eseguito solo una volta; non importa quanti oggetti crei.

Più

 35
Author: Premraj, 2018-04-17 13:01:57

Per una divertente applicazione di inizializzazione double brace, vedere qui Array di Dwemthy in Java.

Un estratto

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

E ora, preparati per il BattleOfGrottoOfSausageSmellse bacon pancetta grossa!

 21
Author: akuhn, 2009-12-24 16:57:24

Penso che sia importante sottolineare che non esiste una cosa come "Inizializzazione doppia parentesi graffa" in Java . Il sito web Oracle non ha questo termine. In questo esempio ci sono due funzionalità utilizzate insieme: classe anonima e blocco inizializzatore. Sembra che il vecchio blocco di inizializzazione sia stato dimenticato dagli sviluppatori e causi una certa confusione in questo argomento. Citazione da Oracle docs :

Blocchi di inizializzazione ad esempio le variabili assomigliano a blocchi di inizializzazione statici, ma senza la parola chiave statica:

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

Vorrei sottolineare che non esiste l'inizializzazione della doppia parentesi graffa. Esiste solo un normale blocco di inizializzazione di una parentesi graffa tradizionale. Il secondo blocco di parentesi graffe non ha nulla a che fare con l'inizializzazione. Le risposte dicono che queste due parentesi inizializzano qualcosa, ma non è così.

In secondo luogo, quasi tutte le risposte parlano che è una cosa usata quando si creano classi interne anonime. Penso che le persone che leggono quelle risposte avranno l'impressione che questo sia usato solo quando creazione di classi interne anonime. Ma è usato in tutte le classi. Leggendo quelle risposte sembra che sia un nuovo futuro speciale dedicato alle classi anonime e penso che sia fuorviante.

Andando oltre, questa domanda parla della situazione in cui la seconda parentesi di apertura si trova subito dopo la prima parentesi di apertura. Quando viene utilizzato in classe normale di solito c'è del codice tra due parentesi graffe, ma è totalmente la stessa cosa. Quindi si tratta di posizionare le parentesi. Quindi penso che non dovremmo dire che questa sia una nuova cosa eccitante, perché questa è la cosa che tutti sappiamo, ma appena scritta con un codice tra parentesi. Non dovremmo creare un nuovo concetto chiamato "inizializzazione doppia parentesi".

Non sono d'accordo con un argomento che crei troppe classi anonime. Non li stai creando perché un blocco di inizializzazione, ma solo perché li crei. Verrebbero creati anche se non si utilizzassero due parentesi graffe in modo che questi problemi si verifichino anche senza inizializzazione... L'inizializzazione non è il fattore che crea l'oggetto inizializzato.

Inoltre non dovremmo parlare del problema creato usando questa cosa inesistente "inizializzazione doppia parentesi" o anche dalla normale inizializzazione di una parentesi, perché i problemi descritti esistono solo a causa della creazione di una classe anonima, quindi non ha nulla a che fare con la domanda originale. Ma tutte le risposte danno ai lettori l'impressione che non sia colpa della creazione di classi anonime, ma di questo male cosa (inesistente) chiamata "inizializzazione doppia parentesi graffa".

 7
Author: ctomek, 2015-11-28 19:11:41

Per evitare tutti gli effetti negativi dell'inizializzazione della doppia parentesi graffa, ad esempio:

  1. Compatibilità "uguale" interrotta.
  2. Nessun controllo eseguito, quando si utilizzano assegnazioni dirette.
  3. Possibili perdite di memoria.

Fai le cose successive:

  1. Crea una classe "Builder" separata specialmente per l'inizializzazione della doppia parentesi graffa.
  2. Dichiara campi con valori predefiniti.
  3. Metti il metodo di creazione dell'oggetto in questo classe.

Esempio:

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

Utilizzo:

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

Vantaggi:

  1. Semplicemente da usare.
  2. Non rompe la compatibilità "uguale".
  3. È possibile eseguire controlli nel metodo di creazione.
  4. Nessuna perdita di memoria.

Svantaggi:

  • Nessuna.

E, di conseguenza, abbiamo il modello più semplice di java builder di sempre.

Vedi tutti gli esempi su github: java-sf-builder-simple-example

 4
Author: Boris Podchezertsev, 2015-09-04 18:19:03

È - tra gli altri usi - una scorciatoia per inizializzare le raccolte. Per saperne di più ...

 3
Author: miku, 2009-12-24 15:14:11

Intendi qualcosa del genere?

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

È un'inizializzazione della lista di array in tempo di creazione (hack)

 3
Author: dhblah, 2011-03-29 16:15:19

Puoi mettere alcune istruzioni Java come loop per inizializzare la raccolta:

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

Ma questo caso influisce sulle prestazioni, controlla questo discussione

 3
Author: Anton Dozortsev, 2017-05-23 12:10:10

Questo sembrerebbe essere lo stesso della parola chiave with così popolare in flash e vbscript. È un metodo per cambiare cosa è this e niente di più.

 0
Author: Chuck Vose, 2009-12-24 16:56:29