Последний счетчик в цикле for?
У меня есть этот код:
List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
r.add(new Runnable() {
@Override
public void run() {
System.out.println(i);
}
});
}
Он явно не компилируется, потому что i
должен быть окончательным, чтобы использоваться в анонимном классе. Но я не могу сделать это окончательным, потому что это не так. Что бы вы сделали? Решение должно дублировать его, но я думал, что может быть лучший способ:
List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
final int i_final = i;
r.add(new Runnable() {
@Override
public void run() {
System.out.println(i_final);
}
});
}
РЕДАКТИРОВАТЬ, чтобы это было ясно, я использовал Runnable здесь для примера, вопрос действительно об анонимных классах, что может быть чем-то еще.
Ответы
Ответ 1
Я думаю, что ваше решение является самым простым способом.
Другой вариант - реорганизовать создание внутреннего класса в функцию factory, которая сделает это за вас, тогда ваш цикл может быть чем-то чистым:
List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
r.add(generateRunnablePrinter(i));
}
И функция factory может просто объявить окончательный параметр:
private Runnable generateRunnablePrinter(final int value) {
return new Runnable() {
public void run() {
System.out.println(value);
}
};
}
Я предпочитаю этот рефакторированный подход, потому что он сохраняет код чище, относительно самоописательно, а также скрывает всю внутреннюю печать.
Случайное отступление: если вы считаете, что анонимные внутренние классы эквивалентны замыканиям, то generateRunnablePrinter
является фактически функцией более высокого порядка. Кто сказал, что вы не можете выполнять функциональное программирование на Java: -)
Ответ 2
Это то, что IntelliJ делает для вас как исправление. Единственное отличие я бы сделал
ExecutorService es =
for(int i = 0; i < 10; i++) {
final int i_final = i;
es.execute(new Runnable() {
Ответ 3
(Менее оптимальная) альтернатива: создайте небольшой внутренний класс, который реализует Runnable
:
class Printer implements Runnable {
private int index;
public Printer(int index) {
this.index = index;
}
public void run() {
System.out.println(index);
}
}
List<Runnable> r = new ArrayList<>();
for(int i = 0; i < 10; i++) {
r.add(new Printer(i));
}
Ответ 4
Ваше решение неплохое. Вы можете сделать другие вещи, например определить свой собственный подкласс Runnable
и инициализировать его с помощью i
в блоке конструктора или инициализации, но в этом случае я думаю, что это просто добавляет сложности без уважительной причины.
BTW: Я предполагаю, что ваш пример является синтетическим, на практике создание нового Runnable
просто для печати целого не показалось бы хорошей идеей.
Ответ 5
Я боюсь, что нет другого способа, кроме копирования вашего счетчика на вторую конечную переменную и использования этого в вашем анонимном внутреннем классе. Это один из "недостатков" Java вокруг темы закрытия и рекламируемого преимущества родственных языков, таких как Groovy.
Ответ 6
Выглядит хорошо. Вы хотите использовать значение переменной цикла внутри вашего анонимного класса, и переменная цикла, очевидно, не может быть окончательной (по мере изменения ее значения).
Создание новой конечной локальной переменной является хорошим решением.