Блоки анонимного кода в Java - PullRequest
60 голосов
/ 14 октября 2009

Есть ли практическое использование блоков анонимного кода в Java?

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

Обратите внимание, что речь идет не о именованных блоках, т.е.

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

.

Ответы [ 8 ]

120 голосов
/ 14 октября 2009

Они ограничивают область видимости переменной.

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

На практике, однако, если вы обнаружите, что используете такой блок кода, это, вероятно, признак того, что вы хотите реорганизовать этот блок в метод.

43 голосов
/ 13 апреля 2011

@ Ответ Дэвида Сейлера верен, но я бы сказал, что кодовые блоки очень полезны и должны использоваться часто и не обязательно указывают на необходимость учитывать метод. Я считаю, что они особенно полезны для построения деревьев компонентов Swing, например:

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

Мало того, что кодовые блоки ограничивают область видимости переменных настолько строго, насколько это возможно (что всегда хорошо, особенно при работе с изменяемыми переменными состояния и неконечными переменными), но они также иллюстрируют иерархию компонентов во многом как XML / HTML облегчает чтение, запись и обслуживание кода.

Моя проблема с выделением каждого экземпляра компонента в метод заключается в том, что

  1. Этот метод будет использоваться только один раз, но для широкой аудитории, даже если это метод частного экземпляра.
  2. Сложнее читать, представляя более глубокое и сложное дерево компонентов, вам придется углубиться в поиск интересующего вас кода, а затем потерять визуальный контекст.

В этом примере Swing я обнаружил, что когда сложность действительно выходит за пределы управляемости, это указывает на то, что пришло время разложить ветвь дерева в новый класс, а не на кучу небольших методов.

23 голосов
/ 17 июня 2013

Обычно лучше всего сделать область видимости локальных переменных как можно меньше . Блоки анонимного кода могут помочь с этим.

Я считаю это особенно полезным с switch заявлениями. Рассмотрим следующий пример без блоков анонимного кода:

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

И с анонимными блоками кода:

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

Я считаю, что вторая версия чище и проще для чтения. И это уменьшает область действия переменных, объявленных внутри коммутатора, до случая, в котором они были объявлены, что, по моему опыту, является тем, что вам нужно в 99% случаев в любом случае.

Тем не менее, имейте в виду, что не изменяет поведение при падении регистра - вам все равно нужно помнить, чтобы включить break или return, чтобы предотвратить это!

16 голосов
/ 14 октября 2009

Я думаю, что вы и / или другие ответы путаете две разные синтаксические конструкции; а именно инициализаторы и блоки экземпляров. (И, кстати, «именованный блок» на самом деле является помеченным оператором, где оператор оказывается блоком.)

Инициализатор экземпляра используется на синтаксическом уровне члена класса; например,

public class Test {
    final int foo;

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

Конструкция Initializer чаще всего используется с анонимными классами в соответствии с примером @ dfa. Другой вариант использования для сложной инициализации атрибутов final; например см. пример выше. (Тем не менее, чаще это делается с помощью обычного конструктора. Приведенный выше шаблон чаще используется со статическими инициализаторами.)

Другая конструкция является обычным блоком и появляется внутри блока кода, такого как метод; например, * +1008 *

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

Блоки чаще всего используются как часть управляющих операторов для группировки последовательности операторов. Но когда вы используете их выше, они (просто) позволяют вам ограничивать видимость объявлений; например j в приведенном выше.

Как правило, это означает, что вам необходимо провести рефакторинг кода, но это не всегда ясно. Например, вы иногда видите подобные вещи в интерпретаторах, написанных на Java. Операторы в плечах переключателя могут быть разделены на отдельные методы, но это может привести к значительному снижению производительности для «внутреннего цикла» интерпретатора; например,

    switch (op) {
    case OP1: {
             int tmp = ...;
             // do something
             break;
         }
    case OP2: {
             int tmp = ...;
             // do something else
             break;
         }
    ...
    };
11 голосов
/ 14 октября 2009

Вы можете использовать его как конструктор для анонимных внутренних классов.

Как это:

alt text

Таким образом, вы можете инициализировать свой объект, так как свободный блок выполняется во время создания объекта.

Он не ограничен анонимными внутренними классами, он также применим к обычным классам.

public class SomeClass {
    public List data;{
        data = new ArrayList();
        data.add(1);
        data.add(1);
        data.add(1);
    }
}
6 голосов
/ 14 октября 2009

Анонимные блоки полезны для ограничения области действия переменной, а также для инициализации двойной скобкой .

Сравнить

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

с

Set<String> validCodes = new HashSet<String>() {{
  add("XZ13s");
  add("AB21/X");
  add("YYLEX");
  add("AR5E");
}};
2 голосов
/ 30 января 2018

Блок инициализации экземпляра:

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

Блок анонимного инициализатора:

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

Ограничение области действия переменной:

class Test {
    public static void main() {
        { int i = 20; }
        System.out.println(i); // error
    }
}
0 голосов
/ 27 марта 2017

Вы можете использовать блок для инициализации конечной переменной из родительской области. Это хороший способ ограничить область действия некоторых переменных, используемых только для инициализации одной переменной.

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
}

В общем, предпочтительно переместить блок в метод, но этот синтаксис может быть полезен для одноразовых случаев, когда необходимо вернуть несколько переменных и вы не хотите создавать класс-оболочку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...