Может быть, этот трюк дает вам идею

aт быть только окончательным здесь. Почему? Как я могу переназначитьa вonClick() метод, не сохраняя его в качестве частного члена?

private void f(Button b, final int a){
    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            int b = a*5;

        }
    });
}

Как я могу вернуть5 * a когда это щелкнуло? Я имею в виду,

private void f(Button b, final int a){
    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             return b; // but return type is void 
        }
    });
}
 Mehrdad19 янв. 2011 г., 08:10
Это то, что я имею в виду - он не поддерживает полное закрытие, потому что не допускает доступ к неконечным переменным.
 Ivan Dubrov19 янв. 2011 г., 08:02
Чего ты пытаешься достичь? Обработчик щелчка может быть выполнен, когда "f" закончен.
 Mehrdad19 янв. 2011 г., 08:00
Я не думаю, что анонимные Java-классы обеспечивают такое лямбда-замыкание, которое вы ожидаете, но кто-то, пожалуйста, поправьте меня, если я ошибаюсь ...
 user46787119 янв. 2011 г., 08:08
@Lambert, если вы хотите использовать a в методе onClick, он должен быть окончательным @Ivan Как метод f () может вести себя как метод onClick (), возвращая int при нажатии
 Peter Lawrey24 февр. 2016 г., 13:41
Примечание: начиная с Java 8 ваша переменная должна быть толькоэффективно окончательный

Ответы на вопрос(13)

осле того, как поток, который его породил, завершился. В вашем примере внутренний класс будет вызываться в потоке диспетчеризации событий, а не в том же потоке, в котором он был создан. Следовательно, область действия переменных будет другой. Поэтому для защиты таких проблем области назначения переменных вы должны объявить их окончательными.

которая позволяет анонимному классу обновлять данные во внешней области.

private void f(Button b, final int a) {
    final int[] res = new int[1];
    b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            res[0] = a * 5;
        }
    });

    // But at this point handler is most likely not executed yet!
    // How should we now res[0] is ready?
}

Однако этот прием не очень хорош из-за проблем с синхронизацией. Если обработчик вызывается позже, вам необходимо: 1) синхронизировать доступ к res, если обработчик был вызван из другого потока; 2) необходимо иметь какой-либо флаг или указатель того, что res был обновлен.

Этот прием работает нормально, если анонимный класс вызывается в том же потоке немедленно. Подобно:

// ...

final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);

// ...
 Ivan Dubrov19 янв. 2011 г., 08:12
Тогда ответ таков: :)
 user46787119 янв. 2011 г., 08:10
спасибо за Ваш ответ. Я знаю все это, и мое решение лучше, чем это. мой вопрос "почему только окончательный"?
 csharpfolk06 авг. 2017 г., 14:35
Вы также можете использовать, например,AtomicReference или жеAtomicInt для той же цели ...
 lcn09 дек. 2015 г., 00:24
Читатьstackoverflow.com/q/12830611/2073130 для хорошего обсуждения обоснования "почему только финал".
 RuntimeException30 окт. 2015 г., 12:54
Благодарю. Я использовал трюк выше самостоятельно. Я не был уверен, если это хорошая идея. Если Java не позволяет этого, то на то может быть веская причина. Ваш ответ уточняет, что мойList.forEach Код безопасен.

рассмотрите следующую программу:

public class Program {

    interface Interface {
        public void printInteger();
    }
    static Interface interfaceInstance = null;

    static void initialize(int val) {
        class Impl implements Interface {
            @Override
            public void printInteger() {
                System.out.println(val);
            }
        }
        interfaceInstance = new Impl();
    }

    public static void main(String[] args) {
        initialize(12345);
        interfaceInstance.printInteger();
    }
}

interfaceInstance остается в памяти послеинициализировать метод возвращает, но параметрвал не. JVM не может получить доступ к локальной переменной вне ее области действия, поэтому Java выполняет последующий вызовprintInteger работать, копируя значениевал к неявному полю с тем же именем вinterfaceInstance,interfaceInstance Говорят, что естьзахваченный значение локального параметра. Если параметр не является окончательным (или фактически окончательным), его значение может измениться, не синхронизироваться с зафиксированным значением, что может привести к неинтуитивному поведению.

Решение Вопроса

часть этого становится неактуальной в Java 8, гдеfinal может быть неявным. Толькофактически Последняя переменная может быть использована в анонимном внутреннем классе или лямбда-выражении.

Это в основном из-за того, как Java управляетукупорочные.

Когда вы создаете экземпляр анонимного внутреннего класса, любые переменные, которые используются в этом классе, имеют своиценности скопировано через автоматически сгенерированный конструктор. Это избавляет компилятор от необходимости автоматически генерировать различные дополнительные типы для хранения логического состояния «локальных переменных», как, например, это делает компилятор C # ... (Когда C # захватывает переменную в анонимной функции, он действительно захватывает переменную - Закрытие может обновить переменную таким образом, который виден основной частью метода, и наоборот.)

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

Если сделать окончательную переменную, все эти возможности будут удалены - поскольку значение не может быть изменено вообще, вам не нужно беспокоиться о том, будут ли такие изменения видны. Единственный способ позволить методу и анонимному внутреннему классу видеть изменения друг друга - это использовать изменяемый тип некоторого описания. Это может быть сам класс включения, массив, изменяемый тип-обертка ... что-нибудь в этом роде. По сути, это немного похоже на общение между одним методом: изменения, внесенные впараметры одного метода не виден вызывающим, но изменения, внесенные в объектыупоминается по параметрам видно.

Если вы заинтересованы в более подробном сравнении между закрытиями Java и C #, у меня естьстатья который идет в это дальше. Я хотел сосредоточиться на стороне Java в этом ответе :)

 Ivan Dubrov19 янв. 2011 г., 08:14
Да уж. По сути, полная поддержка замыканий может быть реализована путем перемещения всех переменных, на которые есть ссылки, в специальный автоматически сгенерированный класс.
 Jon Skeet19 янв. 2011 г., 08:15
@Ivan: Как и C #, в основном. Тем не менее, он имеет достаточную степень сложности, если вам нужна такая же функциональность, как в C #, где переменные из разных областей могут быть "созданы" разное количество раз.
 Mathias Bader24 окт. 2014 г., 16:41
Это все было верно для Java 7, имейте в виду, что с Java 8 были введены замыкания, и теперь действительно возможно получить доступ к неконечному полю класса из его внутреннего класса.
 Thilo04 мар. 2016 г., 08:56
@MathiasBader: Действительно? Я думал, что это все тот же механизм, компилятор теперь достаточно умен, чтобы сделать выводfinal (но это все еще должно быть эффективно окончательно).
 Ustaman Sangat19 сент. 2011 г., 16:04

Джон имеет подробности реализации, другой возможный ответ будет то, что JVM не хочет обрабатывать записи в записи, которые закончили его активацию.

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

Я помню, что в Smalltalk вы бы подняли нелегальный магазин, когда вы сделаете такую ​​модификацию.

чтобы получить возвращаемое значение. я имею в виду

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

Теперь вы можете получить значение K и использовать его там, где хотите.

Ответ вашего почему:

Локальный экземпляр внутреннего класса привязан к классу Main и может обращаться к последним локальным переменным содержащего его метода. Когда экземпляр использует последний локальный элемент своего содержащего метода, переменная сохраняет значение, которое оно содержало во время создания экземпляра, даже если переменная вышла из области видимости (это, по сути, грубая ограниченная версия замыканий Java).

Поскольку локальный внутренний класс не является ни членом класса, ни пакета, он не объявляется с уровнем доступа. (Однако, имейте в виду, что его собственные члены имеют уровни доступа, как в обычном классе.)

 user46787119 янв. 2011 г., 08:11
Я упомянул, что "не оставляя его в качестве частного члена"
private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}

Может быть, этот трюк дает вам идею

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);

все переменные, объявленные как final в области действия этого метода, доступны из внутреннего класса. Для скалярных значений, после того, как оно было назначено, значение конечной переменной не может измениться. Для значений объекта ссылка не может быть изменена. Это позволяет компилятору Java «захватывать» значение переменной во время выполнения и сохранять копию в виде поля во внутреннем классе. После завершения внешнего метода и удаления его стекового фрейма исходная переменная исчезает, но личная копия внутреннего класса сохраняется в собственной памяти класса.

(http://en.wikipedia.org/wiki/Final_%28Java%29)

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

 Mike7627 авг. 2016 г., 15:13
Это не так, как это реализовано в таких языках, как C #, которые поддерживают эту функцию. Фактически, компилятор изменяет переменную с локальной переменной на переменную экземпляра или создает для этих переменных дополнительную структуру данных, которая может превзойти область действия внешнего класса. Тем не менее, нет «множественных копий локальных переменных»
 Adowrath15 сент. 2017 г., 09:30
Mike76 Я не смотрел на реализацию C #, но Scala делает второе, что вы упомянули, я думаю: еслиInt переназначается внутри замыкания, измените эту переменную на экземплярIntRef (по сути изменчивыйInteger обертка). Каждый доступ к переменной затем переписывается соответственно.

в Java переменная может быть конечной не только как параметр, но и как поле уровня класса, например

public class Test
{
 public final int a = 3;

или как локальная переменная, как

public static void main(String[] args)
{
 final int a = 3;

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

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

Вы не можете иметь переменную как finalа также дать ему новое значение.final означает только это: значение является неизменным и окончательным.

И поскольку это окончательно, Java может безопаснокопия это к местным анонимным классам. Вы не получаете немногоссылка к int (особенно если вы не можете иметь ссылки на примитивы типа int в Java, просто ссылки наОбъекты).

Он просто копирует значение a в неявный int, называемый a в вашем анонимном классе.

 Saurabh Oza29 янв. 2019 г., 13:44
мы уже знаем, что финал доступен, но мы хотим знать, почему? не могли бы вы добавить еще несколько объяснений, почему сторона?
 eljenso19 янв. 2011 г., 13:23
Я связываю «переменную уровня класса» сstatic, Может быть, это будет более понятно, если вместо этого использовать «переменную экземпляра».
 Zach L19 янв. 2011 г., 18:01
ну, я использовал уровень класса, потому что техника будет работать как с экземплярами, так и со статическими переменными.

Создайте Array List, поместите в него значение и верните его:

private ArrayList f(Button b, final int a)
{
    final ArrayList al = new ArrayList();
    b.addClickHandler(new ClickHandler() {

         @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             al.add(b);
        }
    });
    return al;
}
 NitinSingh18 июл. 2018 г., 12:21
ОП спрашивает, почему что-то требуется. Следовательно, вы должны указать, как ваш код решает эту проблему.

внутренний класс и строгое правило относится квнутренние классы (JLS 8.1.3):

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

Я еще не нашел причину или объяснение для jls или jvms, но мы знаем, что компилятор создает отдельный файл класса для каждого внутреннего класса, и он должен убедиться, что методы объявлены в этом файле класса ( на уровне байтового кода) по крайней мере, иметь доступ к значениям локальных переменных.

(У Джона полный ответ - Я оставляю этот файл без восстановления, потому что кто-то может заинтересоваться правилом JLS)

Ваш ответ на вопрос