2009-10-13 26 views
56

Esistono alcuni usi pratici dei blocchi di codice anonimi in Java?Blocchi di codice anonimi in Java

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

prega di notare che non si tratta di nome blocchi, vale a dire

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

.

+4

Bene, tendo ad usarli in congiunzione con 'if',' else', 'for',' while', 'do',' switch' e 'case'. –

+5

Incidentalmente, anche io. Ma poi di nuovo, ho deciso di non escludere gli usi che (per me) sembravano ovvi, come quelli che hai menzionato. –

risposta

109

Restringono l'ambito variabile.

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

In pratica, però, se ci si trova con un tale blocco di codice che è probabilmente un segno che si vuole refactoring quel blocco fuori ad un metodo.

+3

Buona risposta e completamente d'accordo con il commento del metodo. –

+4

+1: risposta corretta, ed è un segno di un refattatore disperatamente necessario, alias un "odore di codice", come dice Martin Fowler :) – aperkins

+2

L'unica volta che non si vuole considerare un metodo privato è se il blocco di codice ha bisogno di molto contesto (sotto forma di altre variabili locali). Ma questo è probabilmente un altro odore di codice ... – Thilo

11

È possibile utilizzarlo come costruttore per le classi interne anonime.

Ti piace questa:

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

In questo modo è possibile inizializzare l'oggetto, dal momento che il blocco libero viene eseguito durante la costruzione dell'oggetto.

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

Questo è in realtà un inizializzatore dell'istanza, che è una cosa diversa. –

+1

Diverso da ...? Costruttori? Oh si, intendo "come sostituto o complemento di .." quando si fa riferimento al costruttore. – OscarRyz

+1

diversi per i blocchi di codice all'interno dei metodi come dimostrato nel codice di esempio della domanda. –

6

blocchi anonimi sono utili per limitare la portata della variabile così come per double brace initialization. Confronto:

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

con:

Set<String> validCodes = new HashSet<String>() {{ 
    add("XZ13s"); 
    add("AB21/X"); 
    add("YYLEX"); 
    add("AR5E"); 
}}); 
+1

+1 ma l'ultima parentesi è superflua. – LB40

+2

+1 solo per AR5E –

+0

Whoah, quella doppia inizializzazione è piuttosto carina. – StriplingWarrior

15

te credo e/o le altre risposte sono confuse due costrutti sintattici distinte; 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 inizializzatore di istanze viene utilizzato a livello sintattico di un membro della classe; per esempio.

public class Test { 
    final int foo; 

    { 
     // Some complicated initialization sequence; e.g. 
     int tmp; 
     if (...) { 
      ... 
      tmp = ... 
     } else { 
      ... 
      tmp = ... 
     } 
     foo = tmp; 
    } 
} 

Il costrutto Initializer è più comunemente utilizzato con classi anonime come da esempio di @ dfa. Un altro caso d'uso è per fare un'inizializzazione complicata degli attributi 'finali'; per esempio. vedere 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 compare 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 vengono comunemente utilizzati 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; per esempio. j in quanto sopra.

Questo di solito indica che è necessario rifattorizzare 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. La risposta di

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

Grazie! Potresti per favore espanderne di più o puntare ad alcuni articoli sulla parte "performance hit for the inner loop of the interpreter"? – asgs

+0

La chiamata di un metodo (che non può essere in linea) richiede cicli di CPU aggiuntivi: si tratta di un problema di prestazioni. Potrebbe essere significativo –

37

@ David Seiler ha ragione, ma vorrei sostenere che i blocchi di codice sono molto utili e devono essere utilizzati di frequente e non indicano necessariamente la necessità di scomporre in un metodo. Trovo che sono particolarmente utili per la costruzione di alberi componente Swing, 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 l'ambito delle variabili più strettamente possibile (che è sempre buono, soprattutto quando si tratta di stato mutevole e non -final variabili), ma illustrano anche la gerarchia dei componenti molto nel modo in cui XML/HTML rende il codice più facile da leggere, scrivere e mantenere.

Il mio problema con il factoring fuori ogni instanziazione componente in un metodo è che

  1. Il metodo sarà usato solo una volta ancora esposti ad un pubblico più ampio, anche se si tratta di un metodo di istanza privata.
  2. È più difficile da leggere, immaginando un albero dei componenti più complesso, devi eseguire il drill down per trovare il codice che ti interessa e quindi perdere il contesto visivo.

In questo esempio Altalena, trovo che, quando la complessità realtà non crescere oltre gestibilità indica che è il momento di scomporre un ramo dell'albero in una nuova classe piuttosto che un gruppo di piccoli metodi.

+8

Sto solo lavorando attraverso il codice che usa questa "caratteristica" molto ed è piuttosto difficile da seguire. Penso che sia un pessimo modo di strutturare il codice – Jan

+2

@Jan - ci sono molti modi per usare le buone caratteristiche in modo insoddisfacente, e posso immaginare che questo si stia sfuggendo di mano abbastanza facilmente (nidificazione profonda, scrolling lungo, molte mutazioni, ...) . –

+1

@Steven - Devo ancora vedere un esempio in cui questa funzione non porta a una confusione incomprensibile. Il tuo esempio sopra è ok ma piuttosto piccolo. – Jan

18

Di solito è meglio make the scope of local variables as small as possible. Blocchi di codice anonimi possono aiutare con questo.

Lo trovo particolarmente utile con le istruzioni switch. Si consideri 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 i 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; 
     } 
    } 
} 

ritengo la seconda versione di essere più pulito 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.

Essere avvertito tuttavia, lo fa non cambiamento del comportamento per caso caduta-through - avrai ancora bisogno di ricordarsi di includere un break o return per impedirlo!

+1

Fantastico, grazie! –

1

È possibile 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 sintassi può essere piacevole per una tantum casi in cui più variabili devono essere restituiti e non si vuole creare una classe wrapper.

1

blocco grado di inizializzazione:

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!" 
    } 
} 

Anonimo blocco di inizializzazione:

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<String> 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<String> list = new ArrayList<String>() { 
       private void myCustomMethod(String s1, String s2) { 
        add(s1); 
        add(s2); 
       } 

       { 
        myCustomMethod("el3", "el4"); 
       } 
      }; 

      System.out.println(list); // prints [el3, el4] 
     } 
    } 
} 

portata variabile limitare:

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