Variable used in lambda should be final or effectively final

188
06 января 2018, 02:33

Есть код:

while (true) {
    int i = 0;
    while (i++ < ...) {
        int curr = i;
        futures.add(executor.submit(() -> {
             someMethod(curr);
        }));
    }
}

который отлично компилируется и работает. Однако, если убрать локальную переменную curr:

while (true) {
    int i = 0;
    while (i++ < ...) {
        futures.add(executor.submit(() -> {
             someMethod(i);
        }));
    }
}

компилятор выдаёт ошибку: "variable used in lambda should be final or effectively final". Можно ли как-нибудь обойти это ограничение, не создавая лишнюю локальную переменную (curr), или же это невозможно?

Answer 1

нет, в лямбды надо передавать либо final либо effectively final переменные.

effectively final переменные - те переменные, которые после создания не изменяются внутри кода.

Answer 2

Нет.

Запрещено это т.к. лямбда:() -> {someMethod(i);} буквально означает: «когда будешь выполняться возьми переменную i и отправь ее в someMethod».

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

Запрет и его обоснование указаны в спецификации (JLS §15.27.2):

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
...
Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

Любая локальная переменная, формальный параметр или параметр исключения, использованный, но не объявленный в лямбда выражении, должен либо быть объявлен как final, либо быть фактически финальным ($4.12.4), иначе при попытке его использования генерируется ошибка времени компиляции.
...
Аналогичные правила использования переменных применяются в теле внутреннего класса ($8.1.3). Ограничения на фактически финальные переменные запрещают доступ для динамического изменения локальных переменных, фиксация которых может привести к проблемам параллельности. По сравненнию с ограничением final это снижает ограничения для программистов.

В качестве альтернативы, почему бы не вынести это в метод?

void submit(final int currentNumber) { 
    futures.add(executor.submit(() -> {
         someMethod(currentNumber);
    }));
}
...
int i = 0;
while (i++ < ...) {
     submit(i)
     ...
READ ALSO
Java EE соединиться с удаленной БД MySQL

Java EE соединиться с удаленной БД MySQL

Здравствуйте, пытаюсь соединить с удаленной БД MySql, но выходят ошибки с неправильным указанием адреса, много раз писал в тех поддержку, но все...

184
задача на комбинаторику

задача на комбинаторику

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

285
Сохранение объекта в базу данных с одинаковым полем

Сохранение объекта в базу данных с одинаковым полем

Есть GWT приложение, которое отправляет асинхронный запрос на серверСервер обращается к REST сервису для сохранения переданного объекта в базу...

212