a can only be final here. Why? How can I reassign a in onClick() method without keeping it as private member? private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); } How can I return the 5 * a when it clicked? I mean, 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 } }); }


当前回答

由于Jon有实现细节的答案,另一个可能的答案是JVM不想处理已经结束他的激活的写入记录。

考虑这样一个用例,在这个用例中,你的lambdas不是被应用的,而是被存储在某个地方并稍后运行。

我记得在Smalltalk中,当你做这样的修改时,你会得到一个非法的商店。

其他回答

Java匿名类与Javascript闭包非常相似,但Java以不同的方式实现。(请看安徒生的答案)

所以为了不让Java开发人员对那些有Javascript背景的人的奇怪行为感到困惑。我想这就是为什么他们强迫我们使用final,这不是JVM的限制。

让我们看看下面的Javascript例子:

var add = (function () {
  var counter = 0;

  var func = function () {
    console.log("counter now = " + counter);
    counter += 1; 
  };

  counter = 100; // line 1, this one need to be final in Java

  return func;

})();


add(); // this will print out 100 in Javascript but 0 in Java

在Javascript中,计数器的值是100,因为从头到尾只有一个计数器变量。

但在Java中,如果没有final,它将打印出0,因为在创建内部对象时,0值被复制到内部类对象的隐藏属性中。(这里有两个整数变量,一个在局部方法中,另一个在内部类隐藏属性中)

因此,内部对象创建后的任何更改(如第1行)都不会影响内部对象。所以它会混淆两种不同的结果和行为(Java和Javascript之间)。

我相信这就是为什么,Java决定强制它是最终的,所以数据从开始到结束都是“一致的”。

Java内部类中的final变量[关于]

内部类只能使用

外部类引用 final局部变量来自范围外的引用类型(例如Object…) 值(原语)(例如int…)类型可以由最终引用类型包装。IntelliJ IDEA可以帮助您将其转换为一个元素数组

当编译器生成一个非静态嵌套(内部类)时-一个新类- <OuterClass>$<InnerClass>.class创建并将有界参数传递给构造函数[堆栈上的局部变量],它类似于闭包[Swift about]

Final变量是不能重新赋值的变量。最终引用变量仍然可以通过修改状态来更改

如果这是可能的,那就奇怪了,因为作为一个程序员,你可以做出这样的东西

//Not possible 
private void foo() {

    MyClass myClass = new MyClass(); //Case 1: myClass address is 1
    int a = 5;                       //Case 2: a = 5

    //just as an example
    new Button().addClickHandler(new ClickHandler() {
        
        @Override
        public void onClick(ClickEvent event) {

            /*
            myClass.something(); //<- what is the address - 1 or 2?
            int b = a; //<- what is the value - 5 or 10 ?

            //illusion that next changes are visible for Outer class
            myClass = new MyClass();
            a = 15;
            */
        }
    });

    myClass = new MyClass(); //Case 1: myClass address is 2
    int a = 10; //Case 2: a = 10
}

您可以创建一个类级别的变量来获取返回值。我的意思是

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类,可以访问其包含方法的最终局部变量。当实例使用其包含方法的final局部时,即使变量超出了作用域(这实际上是Java中粗糙的、有限版本的闭包),变量也会保留它在创建实例时所持有的值。

因为局部内部类既不是类的成员也不是包的成员,所以没有使用访问级别声明它。(但是要清楚的是,它自己的成员像普通类一样具有访问级别。)

访问被限制为局部final变量的原因是,如果所有的局部变量都是可访问的,那么它们首先需要被复制到一个单独的部分,在那里内部类可以访问它们,并且维护可变局部变量的多个副本可能会导致数据不一致。而final变量是不可变的,因此任何数量的拷贝都不会对数据的一致性产生任何影响。

在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的意思就是:值是不可改变的和Final的。

由于它是最终的,Java可以安全地将其复制到本地匿名类。你没有得到对int的引用(特别是因为你在Java中不能有对int这样的原语的引用,只能引用对象)。

它只是将a的值复制到一个隐式int,在匿名类中称为a。