在构造函数中调用Swing的add()等可覆盖的方法

Ser*_*nov 23 java swing constructor overriding

我知道从构造函数中调用可覆盖的方法是个坏主意.但我也看到它在Swing中随处可见,其中代码一直add(new JLabel("Something"));出现在构造函数中.

以NetBeans IDE为例.关于构造函数中的可覆盖调用非常挑剔.然而,当它生成Swing代码时,它会将所有这些add()方法调用放入一个initializeComponents()方法中......然后从构造函数中调用它!隐藏问题并禁用警告的好方法(NetBeans没有"从构造函数调用可调覆可覆盖方法的私有方法"警告).但实际上并不是解决问题的方法.

这里发生了什么?我已经做了很多年了,但总是对此感到不安.有没有更好的方法来初始化Swing容器,除了制作一个额外的init()方法(并且不要忘记每次都调用它,这有点无聊)?

这是一个非常人为的例子,说明事情可能出错:

public class MyBasePanel extends JPanel {
    public MyBasePanel() {
        initializeComponents();
    }

    private void initializeComponents() {
        // layout setup omitted
        // overridable call
        add(new JLabel("My label"), BorderLayout.CENTER);
    }
}

public class MyDerivedPanel extends MyBasePanel {
    private final List<JLabel> addedLabels = new ArrayList<>();

    @Override
    public void add(Component comp, Object constraints) {
        super.add(comp);
        if (comp instanceof JLabel) {
            JLabel label = (JLabel) comp;
            addedLabels.add(label); // NPE here
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

hee*_*nee 6

为避免在构造函数中将Swing组件连接在一起,您可以简单地将布线的责任交给另一个对象.例如,您可以向工厂提供接线任务:

public class MyPanelFactory {
    public MyBasePanel myBasePanel() {
        MyBasePanel myBasePanel = new MyBasePanel();
        initMyBasePanel(myBasePanel);
        return myBasePanel;
    }

    public MyDerivedPanel myDerivedPanel() {
        MyDerivedPanel myDerivedPanel = new MyDerivedPanel();
        initMyBasePanel(myDerivedPanel);
        return myDerivedPanel;
    }

    private void initMyBasePanel(MyBasePanel myBasePanel) {
        myBasePanel.add(new JLabel("My label"), BorderLayout.CENTER);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以全力以赴,使用依赖注入容器实例化所有Swing组件,并让容器触发连线.这是Dagger的一个例子:

@Module
public class MyPanelModule {
    static class MyBasePanel extends JPanel {
        private final JLabel myLabel;

        MyBasePanel(JLabel myLabel) {
            this.myLabel = myLabel;
        }

        void initComponents() {
            this.add(myLabel, BorderLayout.CENTER);
        }
    }

    static class MyDerivedPanel extends MyBasePanel {
        private final List<JLabel> addedLabels = new ArrayList<>();

        MyDerivedPanel(JLabel myLabel) {
            super(myLabel);
        }

        @Override
        public void add(Component comp, Object constraints) {
            super.add(comp);
            if (comp instanceof JLabel) {
                JLabel label = (JLabel) comp;
                addedLabels.add(label);
            }
        }
    }

    @Provides MyBasePanel myBasePanel(@Named("myLabel") JLabel myLabel) {
        MyBasePanel myBasePanel = new MyBasePanel(myLabel);
        myBasePanel.initComponents();
        return myBasePanel;
    }

    @Provides MyDerivedPanel myDerivedPanel(@Named("myLabel") JLabel myLabel) {
        MyDerivedPanel myDerivedPanel = new MyDerivedPanel(myLabel);
        myDerivedPanel.initComponents();
        return myDerivedPanel;
    }

    @Provides @Named("myLabel") JLabel myLabel() {
        return new JLabel("My label");
    }
}
Run Code Online (Sandbox Code Playgroud)