Есть два основных способа обойти это. К сожалению, ни один из них не особенно хорош в отношении качества получаемого кода, поэтому я не уверен, что смогу порекомендовать их.
Первым решением было бы сделать exec окончательным одноэлементным массивом. Затем вы можете назначить exec [0] = что-то после объявления, даже если сам массив является окончательным. Вариант этого заключается в использовании / создании некоторого ссылочного класса (поскольку вы можете изменять атрибуты конечных ссылок, но не сами ссылки). Ниже приведен простой пример, но имейте в виду, что он не учитывает никаких проблем параллелизма (см. Далее):
final ScheduledFuture[] exec = new ScheduledFixture[1];
exec[0] = executor.scheduleAtFixedRate( //<< initialized on this line
new Runnable(){
int totalSends = 0;
public void run(){
//do stuff here
if(++totalSends >= repetitions) exec[0].cancel(true); //<< here is says exec might not be initialized
}
},
0, delay, TimeUnit.MILLISECONDS);
В качестве альтернативы вы можете вывести exec из локальной области видимости метода и вместо этого сделать его атрибутом класса.
Однако я должен предупредить вас, что, особенно при начальной нулевой задержке, существует реальная вероятность того, что код внутри исполняемого файла будет выполнен до того, как метод scheduleAtFixedRate вернется, и в этом случае exec [0] будет по-прежнему нулевым. Кроме того, вы должны использовать синхронизацию, чтобы гарантировать, что значение exec [0], установленное основным потоком, будет доступно для потока, ответственного за выполнение runnable.
Оба вышеперечисленных решения должны работать, но я не думаю, что одно из них особенно приятно.