2010-03-28 23 views
2

为什么Elvis elvis的定义必须是在Thread run()方法内部使用的最终定义?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); 
    } 
} 

回答

3

elvis变量的值被匿名内部类捕获。

仅Java(当前)以值捕获变量。编译器要求该变量是最终的,以便在新线程中调用run方法时不会混淆实际使用的内容:如果在创建新线程之后但在启动之前更改了elvis的值,什么你会期望它做到吗?

这是闭包在C#和Java中可用的方式之间的区别。有关详情,请参阅我的closures article。 Java 7将使闭包更加简洁 - 我没有跟随过去,知道是否有任何捕获变量本身的方法,而不是一个特定的值。

+0

捕获变量需要变量实际存储在可捕获的表单中。可以肯定的是,但要求首先使用一种完全不同的方式来编译变量,所以我可以看到不愿误入歧途。 – 2010-03-28 12:17:25

+0

@ Jon,@ Donal变量elvis本身就是枚举,它本来就是一个枚举单身。我会想象它只能初始化一次,并且实际上是最终的? – portoalet 2010-03-28 12:26:56

+2

@portoalet:否。*变量*不是枚举或单例。 *变量*只是一个正常的变量。枚举类型本身可能只能初始化一次,但这与该枚举类型的*变量*在其生命周期中是否可以分配不同的值非常不同。 – 2010-03-28 12:39:47

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。

+0

@Michael变量elvis本身就是枚举,它本来就是一个单例。我会想象它只能初始化一次,并且实际上是最终的? – portoalet 2010-03-28 12:27:27

+0

@portoalet,不,这是不正确的。例如,您可以将null指定给变量,而不仅仅指定Elvis.INSTANCE,所以仍然有必要使其成为最终的。 – 2010-03-28 12:43:36

1

这不是你的问题的一部分,但我只是好奇:为什么你重新分配Elvis.x如果它是一个AtomicInteger?这种错失了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); 
    } 
} 

也因为这有可变的内容,它不应该是一个枚举。

+0

我正在玩Enum Singleton,然后我得到了错误编译(如上面的描述)。所以猫王本意是成为一个可变内容的单身人士。糟糕,是的,我真的应该使用AtomicInteger.set方法,而不是创建一个新的Atomic Integer。发现得好。 – portoalet 2010-03-29 02:19:06