Подтвердить что ты не робот

Анонимные кодовые блоки в Java

Существуют ли какие-либо практические применения анонимных кодовых блоков в Java?

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

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

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

.

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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, что делает код более легким для чтения, записи и поддержки.

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

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

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

Ответ 3

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

Я нахожу это особенно полезным для операторов 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, чтобы предотвратить его!

Ответ 4

Я думаю, что вы и/или другие ответы смешиваете две различные синтаксические конструкции; а именно Instance Initializers и Blocks. (И, кстати, "именованный блок" на самом деле является меткой, где Statement является блоком.)

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

public class Test {
    final int foo;

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

Конструкция Initializer наиболее часто используется с анонимными классами в соответствии с примером @dfa. Другим вариантом использования является сложная инициализация "конечных" атрибутов; например см. пример выше. (Однако чаще это делается с использованием обычного конструктора. Шаблон выше чаще используется со статическими инициализаторами.)

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

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

Ответ 5

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

Как это:

alt text

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

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

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

Ответ 6

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

сравнить

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

Ответ 7

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

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

Ответ 8

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

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
}

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