为什么Elvis elvis定义必须是最终才能在Thread run()方法中使用?
Elvis elvis = Elvis.INSTANCE; // ----> should be final Elvis elvis = Elvis.INSTANCE
elvis.sing(4);
Thread t1 = new Thread(
new Runnable() {
@Override
public void run() {
elvis.sing(6); // --------> elvis has to be final to compile
}
}
);
public enum Elvis {
INSTANCE(2);
Elvis() {
this.x = new AtomicInteger(0);
}
Elvis(int x){
this.x = new AtomicInteger(x);
}
private AtomicInteger x = new AtomicInteger(0);
public int getX() { return x.get(); }
public void setX(int x) {this.x = new AtomicInteger(x);}
public void sing(int x) {
this.x = new AtomicInteger(x);
System.out.println("Elvis singing.." + x);
}
}
答案 0 :(得分:3)
匿名内部类正在捕获elvis
变量的值。
仅限Java(当前)按值捕获变量。编译器要求变量是final的,以便在新线程中调用run
方法时实际使用的内容不会混淆:如果在创建后更改了elvis
的值新线程但在开始之前,你期望它做什么?
这是C#和Java中有效闭包的方式之间的区别。有关详细信息,请参阅我的closures article。 Java 7将使闭包更简洁 - 我一直没有跟踪知道是否有任何方法来捕获变量本身而不是特定值。
答案 1 :(得分:2)
这与线程无关,与构建匿名类有关。问题是您正在匿名类中引用局部变量。现在考虑以下内容:
int c = 5; Runnable r = new Runnable(){ public void run(){ System.out.println(c); } }; c = 6; r.run();
在上面的代码段中,代码是打印5还是打印6?如果r要保持对当前堆栈帧的引用以便解析c,则可以想象它可以打印6.也可以想象它可以更早地绑定/捕获c的值并打印5. Java强制你要做最后的决定,以便使这个完全清楚,并且还要解除Java挂在当前堆栈框架上的需要。
答案 2 :(得分:1)
这不是你问题的一部分,但我只是好奇:如果它是AtomicInteger,你为什么要重新分配Elvis.x?那种错过了AtomicInteger的线程安全性。考虑重写:
public Elvis {
final static private Elvis INSTANCE = new Elvis(2);
static public Elvis getInstance() { return INSTANCE; }
final private AtomicInteger x;
Elvis() { this(0); }
Elvis(int x){
this.x = new AtomicInteger(x);
}
public int getX() { return this.x.get(); }
public void setX(int x) {this.x.set(x); }
public void sing(int x) {
setX(x);
System.out.println("Elvis singing.." + x);
}
}
因为这有可变的内容,所以它不应该是枚举。