2010-03-28 53 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