Слияние двух карт с третьей картой в java лямбда должно быть окончательным или окончательным? [Дубликат]

262
задан alex 5 January 2014 в 21:30
поделиться

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

181
ответ дан Fernando Santos 21 August 2018 в 11:08
поделиться
  • 1
    +1 Примечание: если ссылка не изменена, она фактически является окончательной, даже если объект, на который делается ссылка, изменяется. – Peter Lawrey 5 January 2014 в 22:20
  • 2
    @SURESH ATTA. Почему локальный класс не может получить доступную конечную переменную закрывающего блока? Есть ли способ изменить переменную (которая является ссылочной или примитивной переменной) в локальном классе, если это уже не является фактически окончательной переменной. – stanleyerror 6 February 2015 в 06:36
  • 3
    @stanleyerror Это может помочь: stackoverflow.com/questions/4732544/… – Amit 6 October 2015 в 09:06
  • 4
    Я думаю, что более полезный, чем пример not , фактически окончательный, является примером того, когда что-то является эффективным окончательным. Хотя описание действительно ясно. Вар не обязательно должен быть объявлен окончательным, если ни один код не изменит его значение. – Skychan 16 October 2015 в 15:31
  • 5
    +1 за отличный ответ. Моя любимая работа - использовать классы java.util.concurrent.atomic.AtomicXxxxx, когда я получаю эту ошибку: github.com/GlenKPeterson/UncleJim/wiki/… – GlenPeterson 23 November 2015 в 14:31

Согласно 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;
    }
}
30
ответ дан 5gon12eder 21 August 2018 в 11:08
поделиться
  • 1
    Документы говорят о локальных переменных. bar в вашем примере не является локальной переменной, а полем. & quot; Эффективно окончательный & quot; в сообщении об ошибке, как указано выше, вообще не применяется к полям. – Antti Haapala 1 January 2015 в 19:47
  • 2
    @AnttiHaapala bar - параметр здесь, а не поле. – peter.petrov 11 February 2016 в 23:42

Из статьи 'Brian Goetz',

'Эффективно final' - это переменная, которая не выдавала бы ошибку компилятора, если бы она была добавлена ​​'final'

< / blockquote>

lambda-state-final- Брайан Гетц

20
ответ дан Ajeet Ganga 21 August 2018 в 11:08
поделиться
  • 1
    этот ответ показан как цитата, однако в статье Брайана нет такого точного текста, наверняка не слова appended . Вместо этого это цитата: Неформально локальная переменная является фактически окончательной, если ее начальное значение никогда не изменяется - другими словами, объявить его окончательным, не приведет к сбою компиляции. – lcfd 11 April 2018 в 07:26
  • 2
    Из стенографической копии статьи: Неформально локальная переменная является фактически окончательной, если ее начальное значение никогда не изменяется - другими словами, объявление его окончательным не приведет к сбою компиляции. – Ajeet Ganga 17 April 2018 в 02:00

Когда в выражении лямбда используется назначенная локальная переменная из ее вмещающего пространства, существует важное ограничение. В выражении лямбда может использоваться только локальная переменная, значение которой не изменяется. Это ограничение упоминается как «захват переменной», который описывается как; значения захвата выражения лямбда, а не переменные . Локальные переменные, которые могут использовать выражение лямбда, известны как «эффективно окончательные». Эффективно конечная переменная - это значение, значение которого не изменяется после его первого назначения. Нет необходимости явно объявлять такую ​​переменную как 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;
        };
    }   
}
6
ответ дан Brad Larson 21 August 2018 в 11:08
поделиться
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, компилятор выдаст вам сообщение об ошибке:

Локальные переменные, на которые ссылается выражение лямбда, должны быть окончательными или эффективно окончательный.

1
ответ дан dimo414 21 August 2018 в 11:08
поделиться

Эффективная окончательная тема описана в JLS 4.12.4 , а последний абзац содержит ясное объяснение:

Если переменная окончательно, добавив окончательный модификатор к его объявлению, не будет вводить какие-либо ошибки времени компиляции. И наоборот, локальная переменная или параметр, объявленный окончательно в действительной программе, становится фактически окончательным, если последний модификатор удален.

2
ответ дан Dmitry N. 21 August 2018 в 11:08
поделиться

Эта переменная ниже окончательна, поэтому мы не можем изменить ее значение после инициализации. Если мы попытаемся получить ошибку компиляции ...

final int variable = 123;

Но если мы создадим такую ​​переменную, мы можем изменить ее значение ...

int variable = 123;
variable = 456;

Но в Java 8 все переменные по умолчанию окончательны. Но наличие второй строки в коде делает ее не финальной. Поэтому, если мы удаляем вторую строку из приведенного выше кода, наша переменная теперь «эффективно финальная» ...

int variable = 123;

Итак. Любая переменная, которая назначается один раз и только один раз, является «фактически окончательной ».

12
ответ дан Eurig Jones 21 August 2018 в 11:08
поделиться

Однако, начиная с Java SE 8, локальный класс может обращаться к локальным переменным и параметрам> закрывающего блока, которые являются окончательными или фактически окончательными.

Это не начать с 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
    }
);
-6
ответ дан FiruzzZ 21 August 2018 в 11:08
поделиться
  • 1
    Утверждение ссылается на то, что в & lt; Java 8, доступны только переменные final, но в Java 8 также те, что эффективно final. – Antti Haapala 1 January 2015 в 19:33
  • 2
    Я вижу только код, который не работает, независимо от того, используете ли вы Java 7 или Java 8. – Holger 21 August 2015 в 09:13

Я считаю, что самый простой способ объяснить «эффективно окончательный» - представить себе добавление модификатора final в объявление переменной. Если при этом изменении программа продолжает вести себя одинаково, как во время компиляции, так и во время выполнения, эта переменная является фактически окончательной.

107
ответ дан Maurice Naftalin 21 August 2018 в 11:08
поделиться
  • 1
    Это верно, если понимать «окончательный» java 8, хорошо понят. В противном случае я бы посмотрел на переменную, не объявленную окончательно, что позже вы назначили задание, и ошибочно считаете, что это не окончательный вариант. Вы можете сказать «конечно» ... но не все уделяют столько внимания последним языковым версиям, сколько должны. – fool4jesus 8 June 2015 в 14:09
  • 2
    Единственным исключением из этого правила является то, что локальная переменная, инициализированная константой, не является постоянным выражением для компилятора. Вы не можете использовать такую ​​переменную для случая в коммутаторе / случае, пока вы явно не добавите последнее ключевое слово. Например. "int k = 1; переключатель (someInt) {кейс k: ... ". – Henno Vermeulen 16 October 2015 в 15:17
  • 3
    @HennoVermeulen switch-case не является исключением из правила в этом ответе. Язык указывает, что для case k требуется выражение константа , которое может быть константой константой («Постоянная переменная является конечной переменной примитивного типа или типа String, которая инициализирована с постоянным выражением «g0] JLS 4.12.4 ), который является частным случаем конечной переменной. – Colin D Bennett 3 April 2018 в 19:00
  • 4
    В моем примере компилятор жалуется, что k не является постоянным выражением, поэтому он не может использоваться для коммутатора. При добавлении final поведение компиляции изменяется, потому что теперь оно является постоянной переменной и может использоваться в коммутаторе. Итак, вы правы: это правило все еще правильное. Он просто не применяется к этому примеру и не говорит, является ли k окончательным или нет. – Henno Vermeulen 3 August 2018 в 13:35

Переменная является окончательной или фактически окончательной, когда она инициализируется один раз, и она никогда не мутируется в классе владельца. И мы не можем инициализировать его в циклах или внутренних классах.

Final:

final int number;
number = 23;

Эффективно Final:

int number;
number = 34;
6
ответ дан samadadi 21 August 2018 в 11:08
поделиться

Если вы могли бы добавить модификатор final к локальной переменной, это было эффективно окончательным.

Лямбда-выражения могут получить доступ к

  • статические переменные,
  • переменные экземпляра,
  • эффективные параметры окончательного метода и
  • эффективно конечные локальные переменные.

Источник: OCP: Учебное пособие по программному обеспечению II для программистов SE Certified Professional, Jeanne Boyarsky, Scott Selikoff

Кроме того,

Переменная effectively final - это переменная, значение которой никогда не изменяется, но не объявляется с ключевым словом final.

Источник: Запуск с Java: Из управляющих структур через объекты (6-е издание), Тони Гаддис

Кроме того, не забудьте значение final, что оно инициализируется ровно один раз, прежде чем оно будет использоваться в первый раз.

1
ответ дан snr 21 August 2018 в 11:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: