2012-03-13 42 views
0

我正在使用事件派发线程更新各种swing组件。然而,当我像做以下我得到一个错误 -'局部变量需要声明为final'用于事件派发线程

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

的错误 - “局部变量用户从内部类访问;需要被声明为final”

什么是标准的解决方案对此...只是在每个函数参数之前放置最终?

现在显然我不想声明我的实际用户对象final,因为我将来可能需要修改它。但是可以在函数参数中设置用户对象的最终值吗?

但是当我将它设置为final时,我实际上在做什么,因为这是对我的程序中的'main'用户对象的引用。所以同一个对象在一个地方被宣布为最终的,但在另一个地方不是最终的。是否声明它最终只是表示对它的引用是最终的,但用户对象本身可以自由修改?

回答

4

解决方法是根据您的建议使函数参数最终。

最终修饰符表示在第一次赋值之后,引用不能被重新分配。它对被引用的对象不提供任何保证。

顺便说一句,这是匿名内部课程“陷入”语言的方式的人造物。编译器为每个参数创建隐藏字段,如果你反编译你的代码上面你可以看到发生了什么...

在下面的反编译中,你会看到创建了一个新类Foo $ 1(foo方法#0)并且在#6行它传递了用户arg在它的构造函数中。你会看到类似的把戏事情在美孚的反编译$ 1.class 感叹

从 “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

这是什么标准的解决方案...只是在每个函数参数之前放置最终?

但我是什么实际上做,当我设置到最后,因为这是我的计划,“主”用户对象的引用。所以同一个对象在一个地方被宣布为最终的,但在另一个地方不是最终的。是否声明它最终只是表示对它的引用是最终的,但用户对象本身可以自由修改?

是的。单个对象根本不可能是final,只有变量(基元或引用),类和方法。

2

我通过使用本地变量谈论simpliest方式

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)

1

标准的解决方案,这是你必须让你的变量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()); 
    } 
}); 
} 
相关问题