Анонимные кодовые блоки в Java
Существуют ли какие-либо практические применения анонимных кодовых блоков в Java?
public static void main(String[] args) {
// in
{
// out
}
}
Обратите внимание, что речь идет не о именованных блоках, т.е.
name: {
if ( /* something */ )
break name;
}
.
Ответы
Ответ 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
}
В общем случае предпочтительнее переместить блок в метод, но этот синтаксис может быть приятным для одноразовых случаев, когда нужно возвращать несколько переменных, и вы не хотите создавать класс-оболочку.