Java Swing中ActionListener继承类时如何避免无限循环并访问主类方法?
你遇到的无限循环问题根源很明确——你的MyBtnListener继承了MainClass,而在MainClass的构造方法里又创建了MyBtnListener的实例。当你创建MainClass对象时,会触发它的构造方法,在构造里创建MyBtnListener;而创建MyBtnListener时,因为它继承了MainClass,JVM会先调用父类MainClass的构造方法,这又会再次创建MyBtnListener,如此往复就形成了无限递归的构造调用,自然会输出大量foo(因为每次调用MainClass构造都会执行System.out.println(getFoo()))。
解决思路的核心是:让ActionListener能够访问MainClass的方法,但不要通过继承MainClass的方式,继承在这里完全是多余的,反而引入了循环依赖。下面给你几个常用的可行方案:
方案1:使用匿名内部类(最简洁,适合逻辑简单的场景)
直接在MainClass的构造方法里,用匿名内部类实现ActionListener,这样可以直接访问外部类MainClass的所有成员和方法,不需要额外的类:
class MainClass { String foo; JButton button = new JButton("cool button"); public MainClass(String foo) { this.foo = foo; // 假设这里是JFrame的初始化代码 JFrame frame = new JFrame("Test"); frame.add(button); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); // 用匿名内部类实现ActionListener button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("button clicked"); // 直接访问MainClass的方法和成员 System.out.println("访问MainClass的foo:" + getFoo()); // 直接操作frame变量 frame.dispose(); } }); System.out.println(getFoo()); } public String getFoo() { return this.foo; } }
方案2:使用成员内部类(适合逻辑复杂,需要复用Listener逻辑的场景)
把MyBtnListener定义为MainClass的成员内部类,它同样可以直接访问外部类的成员和方法,而且可以单独维护Listener的逻辑:
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; class MainClass { String foo; JButton button = new JButton("cool button"); public MainClass(String foo) { this.foo = foo; JFrame frame = new JFrame("Test"); frame.add(button); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); button.addActionListener(new MyBtnListener(frame)); System.out.println(getFoo()); } public String getFoo() { return this.foo; } // 成员内部类,不需要继承MainClass class MyBtnListener implements ActionListener { private JFrame target; public MyBtnListener(JFrame target) { this.target = target; } @Override public void actionPerformed(ActionEvent e) { System.out.println("button clicked"); // 直接访问外部类的方法 System.out.println("访问MainClass的foo:" + getFoo()); target.dispose(); } } }
方案3:让Listener持有MainClass的引用(适合Listener需要独立于MainClass,或者在外部定义的场景)
如果你的MyBtnListener需要在MainClass外部定义,那可以让它持有一个MainClass的实例引用,通过这个引用来访问MainClass的方法,而不是继承:
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; class MainClass { String foo; JButton button = new JButton("cool button"); public MainClass(String foo) { this.foo = foo; JFrame frame = new JFrame("Test"); frame.add(button); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); // 创建Listener时传入当前MainClass实例 button.addActionListener(new MyBtnListener(frame, this)); System.out.println(getFoo()); } public String getFoo() { return this.foo; } } // 独立的Listener类,不继承MainClass class MyBtnListener implements ActionListener { private JFrame target; private MainClass mainInstance; // 持有MainClass的引用 public MyBtnListener(JFrame target, MainClass mainInstance) { this.target = target; this.mainInstance = mainInstance; } @Override public void actionPerformed(ActionEvent e) { System.out.println("button clicked"); // 通过引用访问MainClass的方法 System.out.println("访问MainClass的foo:" + mainInstance.getFoo()); target.dispose(); } }
这三个方案都能解决你的问题,核心都是避免ActionListener继承MainClass,从而打破构造方法的无限递归循环,同时保证Listener能正常访问MainClass的方法。你可以根据自己的代码复杂度和需求选择合适的方案。
内容的提问来源于stack exchange,提问作者Obvious_Grapefruit

