2013-10-23 79 views
6

我偶然發現了從匿名內部類獲取值到在外部類中聲明的變量的技巧。它的工作原理,但感覺像一個骯髒的黑客:對匿名內部類使用最終的1元素數組

private int showDialog() 
{ 
    final int[] myValue = new int[1]; 

    JPanel panel = new JPanel(); 
    final JDialog dialog = new JDialog(mainWindow, "Hit the button", true); 
    dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 

    JButton button = new JButton("Hit me!"); 
    button.addActionListener(new ActionListener() 
    { 
     @Override 
     public void actionPerformed(ActionEvent e) 
     { 
      myValue[0] = 42; 
      dialog.setVisible(false); 
     } 
    }); 

    panel.add(button); 
    dialog.add(panel); 
    dialog.pack(); 
    dialog.setVisible(true); 

    return myValue[0]; 
} 

(是的,我知道這個例子可以用一個簡單的JOptionPane更換,但我的實際對話框要複雜得多)的內部函數堅持所有變量它與final交互,但我不能將myValue聲明爲final,因爲內部函數需要爲其分配一個值。聲明它是一個1元素數組解決了這個問題,但好像它可能是一個壞東西TM莫名其妙。我想知道是否a。)這是常見做法,或b。)這樣做可能會導致任何嚴重問題。

回答

0

確實看起來很髒。我無法真正地說出它是多麼「普遍」,我不知道你冒着毀滅世界的危險,但如果我需要這樣的事情,我寧願咬下子彈寫一篇全面的內部類(而不是匿名類)來實現ActionListener。這樣你就可以影響它的封閉類的字段並根據需要調用封閉類中的其他方法。根據你在做什麼,它甚至可能是值得的,只需要全押和子類Dialog來保存這個邏輯。

作爲獎勵,非匿名內部類使調試有點痛苦,因爲你有更多的信息類標識符可供你使用。

0

我不認爲你的代碼有問題。我有時不得不求助於類似的東西,但我使用了一個包裝值的特殊類,我在內部類中調用了一個setter,但最終的結果是相同的。

private static class Result{ 
    private Integer value; 

    //getter and setters here 

} 

.... 

final Result result = new Result(); 

... 
new InnerClass(){ 

    void foo(){ 
     result.setValue(42); 
    } 
} 

問題是內部類只能引用最終變量,因爲它們的內存地址不會改變。

我對你的唯一建議是不要使用int[]作爲值,而是使用Integer[],這樣你就可以知道值0和未設置值(其值=空值)之間的差異。

+0

int'vs'Integer'的好處,但實際上有很多不同的類型(通常不是原始的)可以通過這些對話框訪問,所以這是一件小事。另外,因爲在這種情況下,對話框是模式化的,不能在沒有按下按鈕的情況下關閉,所以幾乎可以保證不成爲問題。 –

3

如果代碼清晰可辨,那我就不會說這樣做太可怕了。

另一種方法是讓JButton在具有showDialog的類中調用一個函數(這是允許的)。該函數可以設置一個將被返回的實例變量。但是這對我來說似乎不太明顯,所以我更喜歡你的方法。

除非你製作了一個深層次的UI框架,否則有時候這些小小的黑客正是你應該做的事。

如果你擔心,你可以做基本相同的事情有私人內部類:

private class DialogReturnValue { 
    public int value; 
} 

private int showDialog() 
{ 
    final DialogReturnValue myValue = new DialogReturnValue(); 

    JPanel panel = new JPanel(); 
    final JDialog dialog = new JDialog(mainWindow, "Hit the button", true); 
    dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 

    JButton button = new JButton("Hit me!"); 
    button.addActionListener(new ActionListener() 
    { 
     @Override 
     public void actionPerformed(ActionEvent e) 
     { 
      myValue.value = 42; 
      dialog.setVisible(false); 
     } 
    }); 

    panel.add(button); 
    dialog.add(panel); 
    dialog.pack(); 
    dialog.setVisible(true); 

    return myValue.value; 
} 

還有還有的ActionListeners看(這很可能是「正確」的方法)。

+0

如果它只是一個,我可以看到這樣做,但有幾十個對話框,每個對話框都有不同類型的控件,並且需要訪問它們的不同要求。 (對話框方法本身實際上大多是'void'返回類型。)我不得不創建很多這些內部類來覆蓋所有我需要的東西。我目前的方法似乎需要最少的代碼。只是想確保它在某些情況下不會中斷。 (例如它是線程安全的嗎?ActionListeners通常會帶來一些多線程問題。) –

+0

不是'actionPerformed',只有在按下按鈕時才調用或者什麼?我看不到在showDialog返回之前按鈕是如何被按下的。 – newacct

+0

據我瞭解(我可能不太對),創建並顯示JDialog會導致線程基本鎖定在'dialog.setVisible(true)'行,直到JDialog關閉,在這種情況下按下按鈕時發生。我所知道的確實是正常工作 - 只要對話框可見,調用showDialog()方法的線程就會停止運行。 –

1

使用AtomicInteger或AtomicReference可以使它很少更好。這實際上是一種常見的做法,但您可以通過引入實現ActionListener的實際類並通過getter提供值來使其更加簡潔。