Mar*_*val 6 java anonymous-class
关于匿名课和最终字段的解释我仍然不满意.有很多问题试图解释明显的问题,但我没有找到所有问题的答案:-)
假设以下代码:
public void method(final int i, int j) {
final int z = 6;
final int x = j;
int k = 5;
new Runnable() {
public void run() {
System.out.print(i);
System.out.print(x);
System.out.print(z);
System.out.print(k);
}
};
}
Run Code Online (Sandbox Code Playgroud)
k属性,无法编译此代码.z在编译期间用声明的值替换属性.当我搜索解决方案时,究竟是如何工作的i,x我发现这个答案说:
然后,编译器可以将匿名类中的lastPrice和price的使用替换为常量的值(在编译时,当然),并且您将不再有访问不存在的变量的问题
它如何适用于字段i,x如果它们是方法的参数?在编译期间不知道它们?这种方法可以发挥作用z.
另一方面,有关于堆栈问题的解释:
这允许Java编译器在运行时"捕获"变量的值,并将副本存储为内部类中的字段.一旦外部方法终止并且其堆栈框架已被移除,原始变量就会消失,但内部类的私有副本仍然存在于类的自身内存中
我会理解匿名类在创建过程中以某种方式复制了所有必需的内容(字段).缺失final有明显的问题,如果匿名类声明下面的某些代码会改变值,则执行使用可能的stale值.
但是,这可以解决当匿名类'方法在使用的属性范围之外执行时的问题.
但这种方法即使没有final声明也应该有效,因为它只是复制所有字段.
这两种方法对我来说都是独立的.说到哪 - 它可以解决我的问题 - 我还没有找到工作final方法领域.即使方法完成,它们也不会从堆栈中删除?对我来说似乎是胡说八道,但它会解释很多事情:-)
什么是正确的答案?
由于需要将变量从方法复制到匿名类(如上所述),因此要求复制的变量是最终的是语言设计决策.因此,无论是方法还是匿名类中的赋值都不会给出陈旧的值,代码会更加一致.
但!在Java 8中,这个要求得到了缓解:final如果变量事实上是最终的,则不再需要:在匿名类中"复制"变量之后不允许赋值.
由于许多功能符号,这是有道理的.否则actionPerformed,当将按钮传播到另一个函数调用时,按钮突然需要其参数是最终的.
在我看来,你在被声明的变量final和它是一个常量之间感到困惑.
编译器不会用常量替换对局部变量的所有引用 - 但是当构造匿名类的实例时,每个相关变量的当前值将传递给构造函数,并存储在匿名类中的变量中.这对于参数和任何其他类型的局部变量一样好.
所以这段代码:
public static void method(final int x) {
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(x);
}
};
r.run();
}
Run Code Online (Sandbox Code Playgroud)
大致相当于:
public static void method(final int x) {
Runnable r = new AnonymousRunnable(x);
r.run();
}
private static class AnonymousRunnable implements Runnable {
private final int x;
AnonymousRunnable(int x) {
this.x = x;
}
@Override public void run() {
System.out.println(x);
}
}
Run Code Online (Sandbox Code Playgroud)
我已经将方法和嵌套类都设置为静态,以避免担心是否this捕获.
局部变量必须在被final捕获时才能避免可能引起混淆的情况.假设情况并非如此 - 请考虑以下示例:
void method() {
int x = 10;
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(x);
}
};
x = 20;
r.run(); // Should this print 10 or 20?
}
Run Code Online (Sandbox Code Playgroud)
使用匿名类的当前工作方式,但只是删除final限制,它将打印10 ...但开发人员可能希望它打印20.同样,您应该考虑如果您x在run方法中修改会发生什么.(如Joop的回答所述,在Java 8中,捕获的局部变量是"有效的最终" - 所以它们表现得好像你已经宣布它们是最终的,但没有明确地这样做.)
作为一种不同方法的示例,C#以不同的方式处理闭包(对于匿名函数),将局部变量提升为一种匿名类,以便可以对它们进行修改.这是一种更复杂的方法,但更灵活一点.您可能会发现我关于Java和C#中的闭包的文章很有用.
| 归档时间: |
|
| 查看次数: |
1762 次 |
| 最近记录: |