Java 初始化方法在主类中的指定方法之前运行
我试图将一个对象从一个控制器传递到另一个控制器。我传递给BookViewController的rowData对象对于initialize方法的工作至关重要,如果没有设置数据,就会出现NullPointerException,这在我的例子中确实发生了 似乎initialize方法在setRowData方法之前运行。正如我所说,initialize方法依赖于setRowData方法的执行 我错过了什么 Main.javaJava 初始化方法在主类中的指定方法之前运行,java,javafx,Java,Javafx,我试图将一个对象从一个控制器传递到另一个控制器。我传递给BookViewController的rowData对象对于initialize方法的工作至关重要,如果没有设置数据,就会出现NullPointerException,这在我的例子中确实发生了 似乎initialize方法在setRowData方法之前运行。正如我所说,initialize方法依赖于setRowData方法的执行 我错过了什么 Main.java public class Main extends Application {
public class Main extends Application {
private static Stage primaryStage;
private static BorderPane mainLayout;
@Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage = primaryStage;
Main.primaryStage.setTitle("BookingApp");
showMainView();
}
private void showMainView() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("MainView.fxml"));
mainLayout = loader.load();
Scene scene = new Scene(mainLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void showBookView(Trip rowData) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("BookView.fxml"));
BorderPane bookTrip = loader.load();
BookViewController bvc = loader.getController();
// The below method never runs.. why?
bvc.setRowData(rowData);
Stage bookStage = new Stage();
bookStage.setTitle("BookingApp");
bookStage.initModality(Modality.WINDOW_MODAL);
bookStage.initOwner(primaryStage);
Scene scene = new Scene(bookTrip);
bookStage.setScene(scene);
bookStage.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
BookViewController.java
public class BookViewController implements Initializable {
private String dbUrl = "jdbc:postgresql://localhost:5432/BookingApp";
private String dbUsername = "postgres";
private String dbPassword = "secret";
@FXML
private TextField personnr;
@FXML
private TextField name;
@FXML
private TextField email;
@FXML
private TextField telnr;
@FXML
private Button addTraveler;
@FXML
private Button removeTraveler;
@FXML
private TableView<Traveler> travelers;
@FXML
private TableColumn<?, ?> personnrColumn;
@FXML
private TableColumn<?, ?> nameColumn;
@FXML
private TableColumn<?, ?> emailColumn;
@FXML
private TableColumn<?, ?> telnrColumn;
@FXML
private TableView<Trip> trips;
@FXML
private TableColumn<?, ?> originColumn;
@FXML
private TableColumn<?, ?> destinationColumn;
@FXML
private TableColumn<?, ?> departureColumn;
@FXML
private TableColumn<?, ?> arrivalColumn;
@FXML
private TableColumn<?, ?> driverColumn;
@FXML
private TableColumn<?, ?> priceAmountColumn;
@FXML
private TableColumn<?, ?> seatsColumn;
@FXML
private Button bookTrip;
private Trip rowData;
private ObservableList<Trip> tripData;
public void setRowData(Trip rowData) {
this.rowData = rowData;
// The below println is never printed to the console..
System.out.println("Test");
}
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
addTraveler.setDisable(true);
removeTraveler.setDisable(true);
populateTravelPlan();
}
@FXML
private void populateTravelPlan() {
try {
Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
tripData = FXCollections.observableArrayList();
ResultSet rs = null;
System.out.println(rowData.getTrip1()); // Here it breaks
System.out.println(rowData.getTrip2());
System.out.println(rowData.getTrip3());
if(rowData.getTrip1() != 0 && rowData.getTrip2() == 0 && rowData.getTrip3() == 0) {
rs = conn.createStatement().executeQuery(
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip1() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"order by departure;");
} else if(rowData.getTrip1() != 0 && rowData.getTrip2() != 0 && rowData.getTrip3() == 0) {
rs = conn.createStatement().executeQuery(
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip1() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"union\r\n" +
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip2() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"order by departure;");
} else if(rowData.getTrip1() != 0 && rowData.getTrip2() != 0 && rowData.getTrip3() != 0) {
rs = conn.createStatement().executeQuery(
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip1() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"union\r\n" +
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip2() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"union\r\n" +
"select trip.tripid, connexion.origin, connexion.destination, trip.departure, trip.arrival, trip.driverpnr, trip.priceamount, trip.seats\r\n" +
"from trip, connexion\r\n" +
"where trip.tripid = " + rowData.getTrip3() + "\r\n" +
"and trip.connexionid = connexion.connexionid\r\n" +
"order by departure;");
}
tripData.add(new Trip(rs.getInt(1), rs.getString(2), rs.getString(3), ""+rs.getTimestamp(4), ""+rs.getTimestamp(5), rs.getString(6), ""+rs.getInt(7), ""+rs.getInt(8)));
while(rs.next()) {
tripData.add(new Trip(rs.getInt(1), rs.getString(2), rs.getString(3), ""+rs.getTimestamp(4), ""+rs.getTimestamp(5), rs.getString(6), ""+rs.getInt(7), ""+rs.getInt(8)));
}
} catch (SQLException e) {
e.printStackTrace();
}
trips.setItems(tripData);
}
}
似乎initialize方法在setRowData方法之前运行
这的确是正确的。fxmloader
调用initialize()
方法作为执行load()
的一部分,这必须在您有机会调用setRowData(…)
之前发生
我错过了什么
没什么,真的
有几个可能的修复方法。一种方法是避免让initialize()
方法依赖于传递给setRowData()
的数据,并将任何依赖于这些数据的代码移到后一种方法。在这种情况下,这是非常简单的:
public void setRowData(Trip rowData) {
this.rowData = rowData;
populateTravelPlan();
}
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
addTraveler.setDisable(true);
removeTraveler.setDisable(true);
// don't do this here:
// populateTravelPlan();
}
如果您不喜欢该解决方案,另一个选项是在代码中而不是在FXML中设置控制器。简单地说,您可以从FXML文件中删除fx:controller
属性,然后在加载FXML时,创建一个控制器实例,调用setRowData()
(或者通过构造函数初始化行数据),然后在fxmloader
上调用setController()
。中详细描述了此解决方案
该解决方案的一个缺点是,如果在FXML设计中使用Scene Builder,Scene Builder将丢失一些功能(因为它不再知道用于创建控制器的类)。如果要保留该功能,可以使用控制器工厂
对于该解决方案,我将修改控制器,以便将行数据传递给构造函数:
public class BookViewController implements Initializable {
// @FXML-annotated fields omitted...
private final Trip rowData;
public BookViewController(Trip rowData) {
this.rowData = rowData ;
}
// remove this, it is now initialized in the constructor
// public void setRowData(Trip rowData) {
// this.rowData = rowData;
// // The below println is never printed to the console..
// System.out.println("Test");
// }
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
addTraveler.setDisable(true);
removeTraveler.setDisable(true);
populateTravelPlan();
}
// etc...
}
此构造函数将阻止fxmloader
使用其默认方法创建控制器实例,因此向加载程序提供控制器工厂以覆盖控制器的创建方式:
public static void showBookView(Trip rowData) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("BookView.fxml"));
loader.setControllerFactory(type -> {
if (type == BookViewController.class) {
return new BookViewController(rowData);
}
// default behavior: need this in case there are <fx:include> in the FXML
try {
return type.getConstructor().newInstance();
} catch (Exception exc) {
// fatal...
throw new RuntimeException(exc);
}
});
BorderPane bookTrip = loader.load();
Stage bookStage = new Stage();
bookStage.setTitle("BookingApp");
bookStage.initModality(Modality.WINDOW_MODAL);
bookStage.initOwner(primaryStage);
Scene scene = new Scene(bookTrip);
bookStage.setScene(scene);
bookStage.showAndWait();
}
public static void showBookView(行程行数据)引发IOException{
FXMLLoader=新的FXMLLoader();
setLocation(Main.class.getResource(“BookView.fxml”);
loader.setControllerFactory(类型->{
if(type==BookViewController.class){
返回新的BookViewController(rowData);
}
//默认行为:如果FXML中存在
试一试{
返回类型.getConstructor().newInstance();
}捕获(异常exc){
//致命的。。。
抛出新的运行时异常(exc);
}
});
BorderPane bookTrip=loader.load();
Stage bookStage=新阶段();
bookStage.setTitle(“BookingApp”);
bookStage.initmodel(model.WINDOW\u model);
bookStage.initOwner(primaryStage);
场景=新场景(bookTrip);
布景舞台(场景);
bookStage.show和wait();
}
您还没有回答自己的问题吗?fxmloader
调用initialize()
方法,作为执行load()
的一部分,因此您可以观察到,在您有机会调用setRowData()
之前会调用该方法。根据对setRowData()
的调用,您可以选择简单地避免使用initialize方法(只需将对populateravelplan()
的调用移动到setRowData()
方法),或者使用控制器工厂。另请参见Ah。。我怎么没想到呢。。谢谢=)
public static void showBookView(Trip rowData) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("BookView.fxml"));
loader.setControllerFactory(type -> {
if (type == BookViewController.class) {
return new BookViewController(rowData);
}
// default behavior: need this in case there are <fx:include> in the FXML
try {
return type.getConstructor().newInstance();
} catch (Exception exc) {
// fatal...
throw new RuntimeException(exc);
}
});
BorderPane bookTrip = loader.load();
Stage bookStage = new Stage();
bookStage.setTitle("BookingApp");
bookStage.initModality(Modality.WINDOW_MODAL);
bookStage.initOwner(primaryStage);
Scene scene = new Scene(bookTrip);
bookStage.setScene(scene);
bookStage.showAndWait();
}