«Локальная переменная должна быть объявлена ​​окончательной» для потока диспетчеризации событий - PullRequest
0 голосов
/ 13 марта 2012

Я обновляю различные компоненты свинга, используя поток диспетчеризации событий. Однако, когда я делаю что-то вроде следующего, я получаю ошибку -

public Foo(User user) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            getNameTextField.setText(user.getName());
        }
     });
}

Ошибка: «локальная переменная доступна для пользователя из внутреннего класса; требуется объявить окончательным»

Каково стандартное решение этого ... просто поставьте final перед каждым параметром функции?

Теперь, очевидно, я не хочу объявлять мой фактический пользовательский объект окончательным, поскольку мне, возможно, потребуется изменить его в будущем. Но можно ли просто установить final для пользовательского объекта в параметре функции?

Но что я на самом деле делаю, когда устанавливаю это в final, поскольку это ссылка на «основной» объект пользователя в моей программе. Таким образом, один и тот же объект объявляется окончательным в одном месте, но не окончательным в другом. Означает ли объявление, что оно окончательно, просто означает, что ссылка на него является окончательной, однако сам пользовательский объект может быть изменен свободно?

Ответы [ 4 ]

4 голосов
/ 13 марта 2012

Решение состоит в том, чтобы сделать аргументы функции окончательными, как вы предлагаете.

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

Кстати, это артефакт того, как анонимные внутренние классы были «вложены» в язык.Компилятор создает скрытые поля для каждого из аргументов. Если вы декомпилируете свой код выше, вы увидите, что происходит ...

В декомпиляции ниже вы увидите, что создан новый класс Foo $ 1 (метод foo #0) и в строке № 6 передается аргумент User в своем конструкторе.Подобные махинации вы увидите в декомпиляции Foo $ 1.class sigh

Скомпилировано из "Foo.java"

public class Foo extends java.lang.Object{

protected javax.swing.JTextField nameTextField;

public Foo();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   return

public void foo(User);
  Code:
   0:   new     #18; //class Foo$1
   3:   dup
   4:   aload_0
   5:   aload_1
   6:   invokespecial   #20; //Method Foo$1."<init>":(LFoo;LUser;)V
   9:   invokestatic    #23; //Method java/awt/EventQueue.invokeLater:        (Ljava/lang/Runnable;)V
   12:  return

}

Скомпилировано из "Foo.java "

class Foo$1 extends java.lang.Object implements java.lang.Runnable{
final Foo this$0;

private final User val$user;

Foo$1(Foo, User);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #14; //Field this$0:LFoo;
   5:   aload_0
   6:   aload_2
   7:   putfield    #16; //Field val$user:LUser;
   10:  aload_0
   11:  invokespecial   #18; //Method java/lang/Object."<init>":()V
   14:  return

public void run();
  Code:
   0:   aload_0
   1:   getfield    #14; //Field this$0:LFoo;
   4:   getfield    #26; //Field Foo.nameTextField:Ljavax/swing/JTextField;
   7:   aload_0
   8:   getfield    #16; //Field val$user:LUser;
   11:  invokeinterface #32,  1; //InterfaceMethod User.getName:()Ljava/lang/String;
   16:  invokevirtual   #38; //Method javax/swing/JTextField.setText:(Ljava/lang/String;)V
   19:  return

}

Вот мой Foo.java (ваш не скомпилирован ...)

import javax.swing.JTextField;

public class Foo {

    protected JTextField nameTextField;

    public void foo(final User user) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                nameTextField.setText(user.getName());
            }
        });
    }
}
2 голосов
/ 13 марта 2012

Я говорю о самых простых способах

1)

public Foo(User user) {

    final String /*(Object, int ....whatever)*/ str = user.getName();

    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            getNameTextField.setText(str);
        }
     });
}

2) с использованием локальной переменной

2 голосов
/ 13 марта 2012

Каково стандартное решение для этого ... просто поставьте финал перед каждым параметром функции?

Да

Но что я на самом деле делаю, когдаЯ установил это как final, так как это ссылка на основной пользовательский объект в моей программе.Таким образом, один и тот же объект объявляется окончательным в одном месте, но не окончательным в другом.Означает ли объявление, что оно окончательно, просто означает, что ссылка на него является окончательной, однако сам пользовательский объект может быть изменен свободно?

Да.Отдельные объекты вообще не могут быть final, только переменные (примитивы или ссылки), классы и методы.

1 голос
/ 13 марта 2012

стандартное решение для этого - вы должны сделать вашу переменную final ..

 inner class need  **user** to be final..  Because of ,
 avoid strange side-effects with closures in java variables referenced by an anonymous 
 delegate must be    marked as final..

одним из лучших способов сделать это ..

public Foo(User user) {
final User user_final=user;
java.awt.EventQueue.invokeLater(new Runnable() {
    public void run() {
        getNameTextField.setText(user_final.getName());
    }
 });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...