如何在Java Swing GUI初始化后更新组件以避免NPE
开发Swing应用时的MVP模式空指针问题及优化方案
我在开发Swing应用时遇到了无法自行解决的问题,尝试遵循Model-Viewer-Presenter(MVP)模式,但可能对该模式的使用存在误解。在Presenter层执行读取文件内容的I/O操作(耗时较长),希望读取完成后将JLabel的内容设置为读取到的文件内容,但文件读取代码执行过快,会在GUI构建完成、JLabel字段初始化前尝试设置其内容,从而引发空指针异常(NPE)。
初始代码示例
Presenter类
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; /** * 实际场景中这里是读取文件,并用读取到的内容调用 * view.updateStatusLabel()。 * 下面的代码会触发NPE,因为view还未完成初始化。 * */ 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(); // 其他GUI初始化操作 // ... } // 由Presenter调用,更新状态标签 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.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>
优化后的代码(移除Guice依赖)
感谢建议后,我重写了代码移除Guice依赖以提升可读性,目前代码可正常运行,但作为Swing新手,希望得到指正。
应用入口Main类
import javax.swing.SwingUtilities; public class Main { public static void main(final String[] args) { SwingUtilities.invokeLater(new View()::createUI); } }
Model类
public class Model { private String password; private boolean initialized; // 此处省略getter和setter方法 }
Observer设计模式接口
interface Observer { void guiInitialized(); } interface Subject { void registerObserver(Observer observer); void notifyObservers(); }
Presenter类
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.setInitialized(true); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } @Override public void guiInitialized() { // 等待GUI初始化完成 new Thread(() -> { while ( !model.isInitialized() ) { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } // 此时GUI已完成初始化 view.updateStatusLabel("init"); }).start(); } }
View类
import java.util.ArrayList; import java.util.List; public class View implements Subject { 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(); // 其他GUI初始化操作 // ... registerObserver(presenter); notifyObservers(); } // 由Presenter调用,更新状态标签 public void updateStatusLabel(final String text) { statusLabel.setText(text); } }
内容的提问来源于stack exchange,提问作者menteith
相关产品推荐
相关产品推荐

