Blocchi di codice anonimi in Java

Ci sono degli usi pratici dei blocchi di codice anonimi in Java?

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

Si noti che non si tratta di blocchi denominati , vale a dire

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

.

Limitano lo scope variabile.

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

In pratica, però, se ti trovi a usare un tale blocco di codice è probabilmente un segno che vuoi rifattorizzare su un metodo.

@David La risposta di Seiler è giusta, ma direi che i blocchi di codice sono molto utili e dovrebbero essere usati frequentemente e non indicano necessariamente la necessità di addentrarsi in un metodo. Trovo che siano particolarmente utili per la costruzione di alberi Swing Component, ad esempio:

 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 solo i blocchi di codice limitano il più ansible l’ambito delle variabili (che è sempre buono, specialmente quando si tratta di variabili mutabili di stato e non finali), ma illustrano anche la gerarchia dei componenti molto nel modo in cui XML / HTML fanno il codice è più facile da leggere, scrivere e mantenere.

Il mio problema con il factoring di ogni componente di istanziazione in un metodo è quello

  1. Il metodo verrà utilizzato solo una volta ancora esposto a un pubblico più ampio, anche se si tratta di un metodo di istanza privato.
  2. È più difficile da leggere, immaginando un albero dei componenti più complesso, dovrai eseguire il drill-down per trovare il codice che ti interessa e quindi perdere il contesto visivo.

In questo esempio di Swing, trovo che quando la complessità supera davvero la gestibilità indica che è tempo di scomporre un ramo dell’albero in una nuova class piuttosto che in una serie di piccoli metodi.

Di solito è meglio rendere il campo di applicazione delle variabili locali il più piccolo ansible . Blocchi di codice anonimi possono aiutare con questo.

Trovo ciò particolarmente utile con le istruzioni switch . Considera il seguente esempio, senza blocchi di codice anonimi:

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

E con blocchi di codice anonimi:

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

Considero la seconda versione più pulita e più facile da leggere. E riduce l’ambito delle variabili dichiarate all’interno del passaggio al caso in cui sono state dichiarate, il che nella mia esperienza è ciò che si desidera il 99% delle volte comunque.

Attenzione, tuttavia, non cambia il comportamento in caso di ricaduta del caso: dovrai ancora ricordarti di includere break o un return per impedirlo!

Penso che tu e / o le altre risposte confondano due distinti costrutti sintattici; ovvero Inizializzatori e blocchi di istanza. (E a proposito, un “named block” è in realtà una dichiarazione etichettata, in cui l’affermazione sembra essere un blocco.)

Un Instance Initializer viene utilizzato a livello sintattico di un membro della class; per esempio

 public class Test { final int foo; { // Some complicated initialization sequence; eg int tmp; if (...) { ... tmp = ... } else { ... tmp = ... } foo = tmp; } } 

Il costrutto Initializer è più comunemente usato con classi anonime come da esempio di @ dfa. Un altro caso d’uso è per fare un’inizializzazione complicata degli attributi ‘finali’; per esempio vedi l’esempio sopra. (Tuttavia, è più comune farlo utilizzando un costruttore normale. Il modello sopra è più comunemente usato con gli inizializzatori statici.)

L’altro costrutto è un blocco ordinario e appare all’interno di un blocco di codice come un metodo; per esempio

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

I blocchi sono comunemente usati come parte delle istruzioni di controllo per raggruppare una sequenza di istruzioni. Ma quando li usi sopra, essi (solo) ti permettono di limitare la visibilità delle dichiarazioni; ad esempio j in quanto sopra.

Questo di solito indica che è necessario refactoring il codice, ma non è sempre chiaro. Ad esempio, a volte si vede questo genere di cose negli interpreti codificati in Java. Le affermazioni nei bracci di commutazione potrebbero essere prese in considerazione in metodi separati, ma ciò potrebbe comportare un significativo calo delle prestazioni per il “ciclo interno” di un interprete; per esempio

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

Puoi usarlo come costruttore per le classi interne anonime.

Come questo:

alt text http://img113.imageshack.us/img113/888/capturadepantalla200910.png

In questo modo puoi inizializzare il tuo object, poiché il blocco libero viene eseguito durante la costruzione dell’object.

Non è limitato alle classi interne anonime, si applica anche alle classi regolari.

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

I blocchi anonimi sono utili per limitare l’ambito di una variabile e per l’ inizializzazione della doppia parentesi .

Confrontare

 Set validCodes = new HashSet(); validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D"); 

con

 Set validCodes = new HashSet() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR5E"); }}; 

Blocco inizializzatore istanza:

 class Test { // this line of code is executed whenever a new instance of Test is created { System.out.println("Instance created!"); } public static void main() { new Test(); // prints "Instance created!" new Test(); // prints "Instance created!" } } 

Blocco inizializzatore anonimo:

 class Test { class Main { public void method() { System.out.println("Test method"); } } public static void main(String[] args) { new Test().new Main() { { method(); // prints "Test method" } }; { //========================================================================= // which means you can even create a List using double brace List list = new ArrayList<>() { { add("el1"); add("el2"); } }; System.out.println(list); // prints [el1, el2] } { //========================================================================== // you can even create your own methods for your anonymous class and use them List list = new ArrayList() { private void myCustomMethod(String s1, String s2) { add(s1); add(s2); } { myCustomMethod("el3", "el4"); } }; System.out.println(list); // prints [el3, el4] } } } 

Limitazione portata variabile:

 class Test { public static void main() { { int i = 20; } System.out.println(i); // error } } 

È ansible utilizzare un blocco per inizializzare una variabile finale dall’ambito principale. Questo è un buon modo per limitare l’ambito di alcune variabili utilizzate solo per inizializzare la singola variabile.

 public void test(final int x) { final ClassA a; final ClassB b; { final ClassC parmC = getC(x); a = parmC.getA(); b = parmC.getB(); } //... a and b are initialized } 

In generale, è preferibile spostare il blocco in un metodo, ma questa syntax può essere utile per casi singoli quando è necessario restituire più variabili e non si desidera creare una class wrapper.