实现ActionListener的Java匿名类?

时间:2009-05-21 04:19:56

标签: java anonymous-class

我最近做了一个编程任务,要求我们在代码中实现一个由UML图指定的程序。有一次,该图指定我必须创建一个显示计数(从1开始)的匿名JButton,并在每次单击时递减。 JButton及其ActionListener都必须是匿名的。

我提出了以下解决方案:

public static void main(String[] args) {
  JFrame f = new JFrame("frame");
  f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  f.setSize(400, 400);

  f.getContentPane().add(new JButton() {

    public int counter;

    {
      this.counter = 1;
      this.setBackground(Color.ORANGE);
      this.setText(this.counter + "");

      this.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
          counter --;
          setText(counter + "");
        }
      });

    }
  });

  f.setVisible(true);

}

这会添加一个匿名JButton,然后添加另一个(内部)匿名ActionListener来处理事件并根据需要更新按钮的文本。有更好的解决方案吗?我很确定我不能声明一个匿名的JButton implements ActionListener (),但还有另一种更优雅的方法来实现相同的结果吗?

6 个答案:

答案 0 :(得分:13)

我通常会这样:

JPanel panel = new JPanel();
panel.add(new JButton(new AbstractAction("name of button") {
    public void actionPerformed(ActionEvent e) {
        //do stuff here
    }
}));

AbstractAction实现了ActionListener,因此它应该满足任务。

将这么多行代码压缩在一起可能是不好的做法,但如果你习惯于阅读它,那么它就会非常优雅。

答案 1 :(得分:4)

这很难看,但您可以使用ActionListener方法和匿名类来执行以下操作:

  f.getContentPane().add(new JButton(new AbstractAction("name of button") {
      private int counter = 0;

      public void actionPerformed(ActionEvent e) {
          ((JButton) e.getSource()).setText(Integer.toString(counter--));
      }
  }) {
      {
          setText("1");
      }
  });

为了更容易访问计数器,您可以将其移出到类的顶层,并从调用setText的两个位置访问它。

答案 2 :(得分:2)

实现多种类型通常是一个坏主意。

很少有必要扩展JComponent类,尽管很多不好的软件和教程都会这样做。最近获得成功的成语/ hack是Double Brace - 一个类只是子类,以便为它提供一个实例初始化器,其作用类似于来自其他语言的with语句。

在这种情况下,相关代码可以写成:

JButton button = new JButton();
button.addActionListener(new ActionListener() {
    int counter = 1;
    {
        updateText();
    }
    public void actionPerformed(ActionEvent arg0) {
        --counter;
        updateText();
    }
    private void updateText()
        setText(Integer.toString(counter));
    }
});
f.getContentPane(button);

如果它变得更复杂,那么你可能想要创建一个外部类(不实现ActionListener或扩展JButton)来处理数据。

另请注意,您应该使用EventQueue.invokeLater样板来确保仅在AWT EDT上使用Swing组件。

答案 3 :(得分:1)

我不会在现实世界的计划中做同样的事情,但考虑到你的任务要求,你很难做得更好。

答案 4 :(得分:1)

有一种更优雅的方式来做到这一点。

不幸的是,它不是Core Java / Swing方法。

您可以使用Groovy中的SwingBuilder来实现相同的结果,使用稍微简洁的语法,例如伪代码:

button(text: '' + counter,
         actionPerformed: {counter--; text = '' + counter + ''},
         constraints:BL.SOUTH)

[http://groovy.codehaus.org/Swing+Builder][1]

我不会在你的任务中使用这个,但是我看到学生真的偏离了常规而得到了标记,但至少你可以把它作为一个可能的途径包括在内进一步调查。

我认为你现在拥有的东西绝对没问题。

答案 5 :(得分:0)

这是仅在家庭作业中被迫做的不良练习任务之一;-)坏事:

  • 使用ActionListener而不是本身不好的Action
  • 因此,范围界定问题起泡
    • 计数器的范围超出必要范围
    • 需要访问actionPerformed中的按钮(通过类型转换或访问周围对象的api)
  • 不可读(又名:不可维护)代码

但是,那么..我们无法抗拒,我们能不能;-)这是一个使用Action的版本,对于前两个问题是干净的(或者我认为),与其他所有例子一样难以理解(当然我作弊了) :首先实现匿名类,然后让IDE执行内联

    f.add(new JButton(new AbstractAction() {

        int counter = 1;
        { // constructor block of action
            updateName();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            counter--;
            updateName();
        }

        private void updateName() {
            putValue(Action.NAME, "" + counter);
        }

    })  { // subclass button 
          {  // constructor block button
            setBackground(Color.PINK);
        }}
    );