Почему инициализаторы экземпляров требуют *final* для внешних переменных в Java?
Какую семантику нарушил бы второй пример, если бы он имел доступ к внешней переменной?
class A {
void f() {
int outer = 1;
// Access non-final outer variable through helper method
new A() {
int inner;
void init(int inner) {
this.inner = inner;
}
}.init(outer); // OK
// Access non-final outer variable through instance initializer
new A() {
{
// int inner = outer; // Does not compile
}
};
outer = 2;
}
}
1 ответ
Это не имеет ничего общего с инициализаторами экземпляров, а связано с тем, что вы захватываете внешнюю локальную переменную. Если вы захватили внешнюю локальную переменную в своем вспомогательном методе, она также не будет работать:
new A() {
int inner;
void init() {
this.inner = outer;
}
}.init();
Когда вы захватываете внешнюю локальную переменную в локальном или анонимном классе, эта внешняя локальная переменная должна быть final
(в Java 7 или раньше) или эффективноfinal
(в Java 8+). Ваша переменная outer
не является final
и (после вашего последнего редактирования) неэффективноfinal
(что означает, что он все равно будет компилироваться, если он был объявлен как final
), потому что вы назначите его позже.
Захваченные локальные переменные должны быть final
или эффективно final
из-за того, как Java реализует захват. В Java, когда создается объект локального или анонимного класса, ему присваиваются любые локальные переменные (как если бы =
) в отдельную независимую копию внутри объекта (потому что объект может пережить локальную область, в которой он был создан). Состояние переменной не "разделяется" между исходной локальной областью и объектами, которые ее захватывают, даже если они имеют одинаковое имя и была объявлена только одна переменная. Поэтому, если бы вы могли присвоить переменной, изменения в одной копии переменной не будут отражены в других копиях, и это будет противоречивым. Чтобы предотвратить это, они запрещают вам назначать любую версию переменной, требуя, чтобы она была эффективной final
,