Разница между final и effectively final
Я играю с lambdas в Java 8, и я наткнулся на предупреждение local variables referenced from a lambda expression must be final or effectively final
. Я знаю, что когда я использую переменные внутри анонимного класса, они должны быть окончательными во внешнем классе, но все же - какая разница между финал и эффективно окончательной?
11 ответов
... начиная с Java SE 8, локальный класс может получить доступ к локальным переменным и параметрам заключительного блока, которые являются окончательными или фактически окончательными. переменная или параметр, значение которого никогда не изменяется после инициализации, является фактически окончательным.
например, предположим, что переменная numberLength
Не объявлено окончательным, и вы добавляете отмеченный оператор присваивания в PhoneNumber
конструктора:
public class OutterClass {
int numberLength; // <== not *final*
class PhoneNumber {
PhoneNumber(String phoneNumber) {
numberLength = 7; // <== assignment to numberLength
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
...
}
...
}
из-за этого оператор присваивания, переменная numberLength больше не является фактически окончательной. в результате компилятор Java генерирует сообщение об ошибке, подобное "локальные переменные, на которые ссылается внутренний класс, должны быть окончательными или фактически окончательными" где внутренний класс PhoneNumber пытается получить доступ к numberLength переменная:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
Я нахожу самый простой способ объяснить "эффективно окончательный" - это представить добавление final
модификатор объявления переменной. Если с этим изменением программа продолжает вести себя одинаково, как во время компиляции, так и во время выполнения, то эта переменная фактически является окончательной.
по словам docs:
переменная или параметр, значение которого никогда не изменяется после инициализации, является фактически окончательным.
в принципе, если компилятор обнаруживает, что переменная не появляется в назначениях вне ее инициализации, то переменная считается эффективно окончательной.
например, рассмотрим некоторый класс:
public class Foo {
public void baz(int bar) {
// While the next line is commented, bar is effectively final
// and while it is uncommented, the assignment means it is not
// effectively final.
// bar = 2;
}
}
из статьи Брайана Гетца,
'Effectively final' - это переменная, которая не даст ошибки компилятора если бы оно было добавлено "окончательным"
эта переменная ниже финал, так что мы не можем изменить его значение после инициализации. Если мы попытаемся мы получим ошибку компиляции...
final int variable = 123;
но если мы создадим такую переменную, мы можем изменить ее значение...
int variable = 123;
variable = 456;
а в Java 8 все переменные являются финал по умолчанию. Но наличие 2-й строки в коде делает его на. Поэтому, если мы удалим 2-ю строку из вышеуказанного кода, наша переменная сейчас "окончательным"...
int variable = 123;
Так.. любая переменная, назначенная один раз и только один раз, является "фактически окончательной".
переменная финал или эффективно окончательной, когда инициализации после и никогда мутировал в своем классе владельца. И мы!--3-->не удалось инициализировать его в петли или внутренние классы.
финал:
final int number;
number = 23;
Эффективно Окончательной:
int number;
number = 34;
когда лямбда-выражение использует назначенную локальную переменную из окружающего пространства, существует важное ограничение. Лямбда-выражение может использовать только локальную переменную, значение которой не изменяется. Это ограничение называется"переменной захвата", который описывается как; значения захвата лямбда-выражения, а не переменные.
Локальные переменные, которые может использовать лямбда-выражение, известны как"эффективно окончательной".
Эффективно конечная переменная-это та, значение которой не изменяется после ее первого назначения. Нет необходимости явно объявлять такую переменную как final, хотя это не будет ошибкой.
Давайте посмотрим на это на примере, у нас есть локальная переменная i, которая инициализируется значением 7, причем в лямбда-выражении мы пытаемся изменить это значение, назначив новое значение i. Это приведет к ошибке компилятора -"локальная переменная i, определенная в охватывающей области, должна быть окончательной или эффективно окончательной"
@FunctionalInterface
interface IFuncInt {
int func(int num1, int num2);
public String toString();
}
public class LambdaVarDemo {
public static void main(String[] args){
int i = 7;
IFuncInt funcInt = (num1, num2) -> {
i = num1 + num2;
return i;
};
}
}
эффективное окончательной тема описана в JLS 4.12.4 и последний абзац состоит из ясного объяснения:
Если переменная является фактически окончательной, добавление окончательного модификатора в ее объявление не приведет к ошибкам во время компиляции. И наоборот, локальная переменная или параметр, объявленный final в допустимой программе, становится final, если удалить окончательный модификатор.
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
// The following statement causes the compiler to generate
// the error "local variables referenced from a lambda expression
// must be final or effectively final" in statement A:
//
// x = 99;
}
}
}
как уже говорили другие, переменная или параметр, значение которого не изменяется после инициализации является окончательным. В приведенном выше коде, если вы измените значение x
во внутреннем классе FirstLevel
тогда компилятор выдаст вам сообщение об ошибке:
локальные переменные, на которые ссылается лямбда-выражение, должны быть окончательными или фактически окончательными.
если бы вы могли добавить
final
модификатор для локальной переменной, это было эффективно окончательной.
лямбда-выражения могут получить доступ
статические переменные,
переменные экземпляра,
эффективно окончательной параметры метода и
эффективно окончательной локальная переменная.
кроме того,
An
effectively final
переменная-это переменная, значение которой не изменено, но не объявлено сfinal
ключевое слово.
источник: начиная с Java: от структур управления через объекты (6-е издание), Тони Геддис
кроме того, не забывайте, что смысл final
что он инициализируется ровно один раз, прежде чем он будет использоваться в первый раз.
однако, начиная с Java SE 8, локальный класс может получить доступ к локальным переменным и параметрам блока >enclosing, которые являются окончательными или фактически окончательными.
это не началось на Java 8, я использую это с давних пор. Этот код, используемый (до java 8), чтобы быть законным:
String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String ann = str; // <---- error, must be final (IDE's gives the hint);
String ann = strFin; // <---- legal;
String str = "legal statement on java 7,"
+"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl.";
//we are forced to use another name than str
}
);