代码之家  ›  专栏  ›  技术社区  ›  menteith

如何在GUI初始化后更新Swing组件以避免Java Swing中的NPE?

  •  0
  • menteith  · 技术社区  · 1 年前

    在开发Swing应用程序时,我遇到了一个自己无法处理的问题。我正在努力坚持 Model-Viewer-Presenter 图案请记住,我可能误解了如何利用这种模式。在 Presenter 层我正在读取文件内容(I/O操作,相当长)。阅读完成后,我想设置的内容 JLabel 到从文件中读取的内容。看起来很容易。然而,读取文件内容的代码非常快,它试图在构建GUI和初始化类中的JLabel字段之前设置JLabel域的内容。

    import javax.swing.SwingUtilities;
    
    public class Presenter {
    
       private final View view;
       private final Model model;
    
       @Inject
       public Presenter(final View view, final Model model) {
          this.view = view;
          this.model = model;
          /**
           * In reality, I have here reading a file and invoking
           * view.updateStatusLabel() with its content.
           * The following code will produces NPE as view has not been initialized.
           * */
          SwingUtilities.invokeLater(() -> view.updateStatusLabel("test"));
       }
    
    }
    

    这是 View GUI初始化的部分:

    public class View {
    
       private final Presenter presenter;
    
       private JLabel statusLabel;
       private JTextField inputField;
    
       public View() {
          this.presenter = new Presenter(this, new Model());
       }
    
    
       public void createUI() {
          this.statusLabel = new JLabel();
          // other GUI initilization
          // ...
       }
       //called by the presenter to update the status label
       public void updateStatusLabel(final String text) {
            statusLabel.setText(text);
       }
    }
    

    应用 main 方法:

    import com.google.inject.Guice;
    import com.google.inject.Inject;
    import lombok.RequiredArgsConstructor;
    
    import javax.swing.SwingUtilities;
    
    @RequiredArgsConstructor(onConstructor = @__(@Inject))
    public class Main {
    
       private final View view;
    
       public static void main(final String[] args) {
    
          final Main main = Guice.createInjector().getInstance(Main.class);
    
          SwingUtilities.invokeLater(main.view::createUI);
       }
    }
    

    如果这是重要的事情,我使用 Guice 以覆盖依赖项注入。即使没有 Guice 就会出现这个问题。以下是我的依赖项:

    <guice.version>7.0.0</guice.version>
    <dependency>
          <groupId>com.google.inject</groupId>
          <artifactId>guice</artifactId>
          <version>${guice.version}</version>
        </dependency>
    
        <dependency>
          <groupId>com.google.inject.extensions</groupId>
          <artifactId>guice-assistedinject</artifactId>
          <version>${guice.version}</version>
        </dependency>
    
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.28</version>
          <scope>provided</scope>
        </dependency>
    

    编辑

    感谢@Gilbert勒布朗克的评论,我设法重写了代码以避免NPE。请参阅下面的更新代码。我通过删除了依赖项注入 Guice 这样代码现在读起来(我希望如此)就更容易了。代码按预期工作。然而,由于我是Swing的新手,我可能犯了一些错误,希望有人能指出这些错误,以便纠正。

    应用程序起点:

    import javax.swing.SwingUtilities;
    
    public class Main {
    
       public static void main(final String[] args) {
    
          SwingUtilities.invokeLater(new View()::createUI);
       }
    }
    

    型号:

        public class Model {
        
           private String password;
           private boolean initiliaized;
    
           // getters and setters here
    
           
        }
    

    所需的接口 Observer 设计模式:

    interface Observer {
       void guiInitialized();
    }
    
    interface Subject {
    
       void registerObserver(Observer observer);
    
       void notifyObservers();
    }
    

    节目主持人

    public class Presenter implements Observer {
    
       private final View view;
       private final Model model;
    
       public Presenter(final View view, final Model model) {
          this.view = view;
          this.model = model;
          new Thread(() -> {
             try {
                Thread.sleep(5_000);
                model.setPassword("password");
                model.setInitiliaized(true);
             } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
             }
          }).start();
       }
    
       @Override
       public void guiInitialized() {
          // wait until gui is initialized
          new Thread(() -> {
             while ( !model.isInitiliaized() ) {
                try {
                   Thread.sleep(500);
                } catch (InterruptedException e) {
                   Thread.currentThread().interrupt();
                }
             }
             // GUI is initliazed by now
             view.updateStatusLabel("init");
          }).start();
       }
    
    }
    

    看法

    public class View {
    
       private final Presenter presenter;
    
       private final List<Observer> observers = new ArrayList<>();
       
    
       private JLabel statusLabel;
       private JTextField inputField;
    
       public View() {
          this.presenter = new Presenter(this, new Model());
       }
    
       @Override
       public void registerObserver(final Observer observer) {
          this.observers.add(observer);
       }
    
       @Override
       public void notifyObservers() {
          observers.forEach(Observer::guiInitialized);
       }
    
    
       public void createUI() {
          this.statusLabel = new JLabel();
          // other GUI initilization
          // ...
    
          registerObserver(presenter);
    
          notifyObservers();
       }
    
       //called by the presenter to update the status label
       public void updateStatusLabel(final String text) {
          statusLabel.setText(text);
       }
    }
    
    0 回复  |  直到 1 年前