我的Trade
类具有以下代码:
public class Trade{
// attributes of each trade that go into the Transaction log
// properties
private ObjectProperty<LocalDate> transactionDate;
private StringProperty itemNum;
private StringProperty buySell;
private DoubleProperty volume;
private DoubleProperty price;
private DoubleBinding transactionFee;
public Trade(BuySell buySell, LocalDate transactionDate, int itemNum, double volume, double price){
this.buySell = new SimpleStringProperty(buySell.toString());
this.transactionDate = new SimpleObjectProperty<LocalDate>(transactionDate);
this.itemNum = new SimpleStringProperty(itemNum));
this.volume = new SimpleDoubleProperty(volume);
this.price = new SimpleDoubleProperty(price);
}
...
}
现在在我的Controller类中,我有以下内容,并且在添加
observableListOfTrades.addAll
语句之前,一切工作都很好,代码破裂并给了我很长的NullPointerException列表。我不知道如何解决这个问题。 public class Controller implements Initializable{
ObservableList<Trade> observableListOfTrades =FXCollections.observableArrayList(trade ->
new Observable[]{
trade.transactionDateProperty(),
trade.stockTickerProperty(),
trade.buySellProperty(),
trade.volumeProperty(),
trade.priceProperty(),
trade.transactionFeeProperty()
}
);
....
....
public void initialize(URL fxmlFileLocation, ResourceBundle resources){
...
...
observableListOfTrades.addAll(new Trade(BuySell.Sell, LocalDate.now().minusDays(5),50,99,99),new Trade(BuySell.Buy, LocalDate.now(),50,1,1));
//<--- Entire Code used to work fine until I added the addAll shown above.
observableListOfTrades.addListener(new ListChangeListener<Trade>() {
@Override
public void onChanged(ListChangeListener.Change change) {
System.out.println("Detected a change! ");
}
});
...
}
这是错误消息:
javafx.fxml.LoadException:
/Users/Eclipse/StockTrackerJavaFX/bin/application/Interface.fxml
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
at application.Main.start(Main.java:27)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
at com.sun.javafx.application.LauncherImpl$$Lambda$54/1508757224.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl$$Lambda$47/186276003.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
at com.sun.javafx.application.PlatformImpl$$Lambda$49/223236686.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
at com.sun.javafx.application.PlatformImpl$$Lambda$48/237061348.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Caused by: java.lang.NullPointerException
at com.sun.javafx.collections.ElementObserver.attachListener(ElementObserver.java:80)
at com.sun.javafx.collections.ObservableListWrapper.doAdd(ObservableListWrapper.java:100)
at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:151)
at java.util.AbstractList.add(AbstractList.java:108)
at java.util.AbstractCollection.addAll(AbstractCollection.java:344)
at javafx.collections.ModifiableObservableListBase.addAll(ModifiableObservableListBase.java:99)
at javafx.collections.ObservableListBase.addAll(ObservableListBase.java:245)
at application.Controller.initialize(Controller.java:233)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
... 18 more
更新:正如@James_D所指出的,
transactionFeeProperty
返回一个null
值。所以,我给那些方法的代码public double getVolume(){
return this.volume.get();
}
public DoubleProperty volumeProperty(){
return this.volume;
}
public void setVolume(double volume){
this.volume.set(volume);
}
public double getPrice(){
return this.price.get();
}
public DoubleProperty priceProperty(){
return this.price;
}
public void setPrice(double price){
this.price.set(price);
}
public double getTransactionFee(){
this.transactionFee = this.price.multiply(this.volume).multiply(0.5);
return this.transactionFee.getValue();
}
public DoubleBinding transactionFeeProperty(){
return this.transactionFee;
}
最佳答案
由于使用提取器创建了ObservableList
,因此将Trade
添加到列表中时:
observableListOfTrades.add(trade);
(或与
addAll(...)
类似),列表将使用提取程序返回的属性注册一个侦听器。即它基本上调用trade.transactionDateProperty().addListener(...);
trade.stockTickerProperty().addListener(...);
// ...
trade.transactionFeeProperty().addListener(...);
因此,确保添加对象时这些方法不返回
null
是很重要的(让属性访问器方法返回null
实际上是一个坏主意)。定义
transactionFeeProperty()
和getTransactionFee()
方法的方式,仅在首次调用transactionFee
之后初始化getTransactionFee()
,直到那时,transactionFeeProperty()
返回null
。因此,当您将new Trade(...)
添加到列表中时,就会出现NullPointerException
。一种解决方法是初始化构造函数中的
transactionFee
以及其他属性:public Trade(BuySell buySell, LocalDate transactionDate, int itemNum, double volume, double price){
this.buySell = new SimpleStringProperty(buySell.toString());
this.transactionDate = new SimpleObjectProperty<LocalDate>(transactionDate);
this.itemNum = new SimpleStringProperty(itemNum));
this.volume = new SimpleDoubleProperty(volume);
this.price = new SimpleDoubleProperty(price);
this.transactionFee = this.price.multiply(this.volume).multiply(0.5);
}
然后就
public double getTransactionFee(){
return this.transactionFee.getValue();
}
public DoubleBinding transactionFeeProperty(){
return this.transactionFee;
}
其他几点。
首先,
transactionFee
仅在price
或volume
更改时才会更改。由于您要在提取程序中返回这些属性,因此实际上也要返回transactionFeeProperty()
是多余的,因为如果transactionFee
发生更改,则price
或volume
必须已更改,并且列表将已经收到更新通知。其次,
xxxProperty()
方法应该真正返回属性,而不是Binding
。表示绑定(或从属)属性的常规方法是使用ReadOnlyDoubleWrapper
。因此,您应该遵循以下几点:public class Trade{
// attributes of each trade that go into the Transaction log
// properties
private ObjectProperty<LocalDate> transactionDate;
private StringProperty itemNum;
private StringProperty buySell;
private DoubleProperty volume;
private DoubleProperty price;
private ReadOnlyDoubleWrapper transactionFee;
public Trade(BuySell buySell, LocalDate transactionDate, int itemNum, double volume, double price){
this.buySell = new SimpleStringProperty(buySell.toString());
this.transactionDate = new SimpleObjectProperty<LocalDate>(transactionDate);
this.itemNum = new SimpleStringProperty(itemNum));
this.volume = new SimpleDoubleProperty(volume);
this.price = new SimpleDoubleProperty(price);
this.transactionFee = new ReadOnlyDoubleWrapper();
this.transactionFee.bind(this.price.multiply(this.volume).multiply(0.5));
}
public double getVolume(){
return this.volume.get();
}
public DoubleProperty volumeProperty(){
return this.volume;
}
public void setVolume(double volume){
this.volume.set(volume);
}
public double getPrice(){
return this.price.get();
}
public DoubleProperty priceProperty(){
return this.price;
}
public void setPrice(double price){
this.price.set(price);
}
// etc etc...
public ReadOnlyDoubleProperty transactionFeeProperty() {
return this.transactionFee.getReadOnlyProperty();
}
public final double getTransactionFee() {
return transactionFeeProperty().get();
}
}
和
ObservableList<Trade> observableListOfTrades =FXCollections.observableArrayList(trade ->
new Observable[]{
trade.transactionDateProperty(),
trade.stockTickerProperty(),
trade.buySellProperty(),
trade.volumeProperty(),
trade.priceProperty(),
}
);
以下内容在功能上是等效的(出于某些原因,我会让您知道...):
ObservableList<Trade> observableListOfTrades =FXCollections.observableArrayList(trade ->
new Observable[]{
trade.transactionDateProperty(),
trade.stockTickerProperty(),
trade.buySellProperty(),
trade.transactionFeeProperty(),
}
);