为什么只有最终变量可以在匿名类中访问?

388
  1. a在这里只能是final。为什么?如何在onClick()方法中重新分配a而不保留它作为私有成员变量?

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 
        }
    });
}

1
我认为Java匿名类并没有提供你所期望的Lambda闭包,但如果我错了,请有人纠正我... - user541686
5
你想要实现什么?当“f”完成时,点击处理程序可以被执行。 - Ivan Dubrov
2
这就是我的意思 - 它不支持完全闭包,因为它不允许访问非 final 变量。 - user541686
4
从Java 8开始,你的变量只需要是 有效地不可变的 - Peter Lawrey
显示剩余3条评论
16个回答

0

尝试这段代码:

创建一个数组列表,将值放入其中并返回:

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;
}

OP正在询问为什么需要某些东西。因此,您应该指出您的代码是如何解决它的。 - NitinSingh

0

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

考虑这样一种情况:你的lambda表达式不是应用程序,而是存储在某个地方并稍后运行。

我记得在Smalltalk中,当您进行此类修改时,会引发非法存储异常。


0

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

内部类只能使用:

  1. 外部类的引用
  2. 来自超出范围的最终本地变量,这些变量是引用类型(例如Object...)
  3. 值(原始)(例如int...)类型可以由最终引用类型包装IntelliJ IDEA可以帮助您将其转换为一个元素的数组

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

final变量是一个不能被重新赋值的变量。但是,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
}

0

Java的匿名类与JavaScript闭包非常相似,但Java的实现方式不同(请参考Andersen的答案)。

因此,为了避免让来自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中,counter的值将为100,因为从开始到结束只有一个counter变量。
但是在Java中,如果没有使用final,它将打印出0,因为在创建内部对象时,0值被复制到内部类对象的隐藏属性中。(这里有两个整数变量,一个在本地方法中,另一个在内部类的隐藏属性中)
因此,在内部对象创建后进行的任何更改(如第1行),都不会影响内部对象。这将导致Java和JavaScript之间产生两种不同的结果和行为之间的混淆。
我相信这就是为什么Java决定强制使用final,以便数据从开始到结束保持“一致”的原因。

0
 public class Closure {
  public void publicMethod() {
    Integer localVariable1 = 10;

    Map<String, Integer> map = new HashMap<String, Integer>() {
      {
        put("a", localVariable1);
      }
    };

    Thread t = new Thread(new Runnable() {
      public void run() {
        System.out.println(localVariable1);
      }
    });

    List<String> list = Arrays.asList("A", "B", "C");

    Collections.sort(list, new Comparator<String>() {
      public int compare(String p1, String p2) {
        return p1.compareTo(p2);
      }
    });
  }
}

编译后,上述代码将生成4个类(3个AIC和1个父类)。
在运行时,如果Java允许更新localVariable1或list,那么编译后的类将变得无效,并且map中将包含过时的数据。
为了避免这种不一致的行为,Java强制要求它们是final或者实际上是final的。

-2

也许这个技巧能给你一些灵感

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接