移除LineChart<String,String>数据时出现String转Number的ClassCastException原因
JavaFX LineChart 移除数据时ClassCastException问题解析
问题描述
我有一个JavaFX应用,想实现点击按钮时逐步移除LineChart<String,String>中的首个数据,让数据递减展示。但首次点击按钮就抛出了ClassCastException,提示String无法转成Number,可我明明没使用Number类型数据。
错误堆栈
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap') at javafx.controls@18/javafx.scene.chart.LineChart.createDataRemoveTimeline(LineChart.java:503) at javafx.controls@18/javafx.scene.chart.LineChart.dataItemRemoved(LineChart.java:359) at javafx.controls@18/javafx.scene.chart.XYChart.dataItemsChanged(XYChart.java:505) at javafx.controls@18/javafx.scene.chart.XYChart$Series$1.onChanged(XYChart.java:1474) ...(省略后续重复堆栈内容)
应用代码
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.LineChart; import javafx.scene.chart.XYChart; import javafx.scene.control.Button; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class MyApplication extends Application { @Override public void start(Stage stage) { LineChart<String,String> lineChart=createLineChart(); Button button = new Button("BUTTON"); button.setOnAction(event -> lineChart.getData().get(0).getData().remove(0)); VBox vBox=new VBox(button,lineChart); Scene scene = new Scene(vBox); stage.setScene(scene); stage.show(); } LineChart<String,String> createLineChart(){ LineChart<String, String> lineChart = new LineChart<>(new CategoryAxis(), new CategoryAxis()); lineChart.getData().add(new XYChart.Series<>()); for (int i = 0; i < 3; i++) { lineChart.getData().get(0).getData().add(new XYChart.Data<>("X string %d".formatted(i+1), "Y string %d".formatted(i+1))); } lineChart.getXAxis().setTickLabelFont(new Font(20)); lineChart.getYAxis().setTickLabelFont(new Font(20)); return lineChart; } public static void main(String[] args) { launch(); } }
问题原因
LineChart本质是数值型折线图,它的设计初衷是基于连续的数值数据绘制线条。哪怕你泛型声明为<String,String>,底层在处理数据移除时的动画逻辑(比如createDataRemoveTimeline方法),会尝试将Y轴的数据当作Number来计算动画的位置过渡——因为折线图的线条位置依赖坐标数值计算,这是LineChart内部的硬性要求,和你声明的泛型无关。
另外,你用CategoryAxis作为Y轴也不合适:CategoryAxis是用来展示离散类别的(比如不同的分类标签),而LineChart的Y轴需要的是能进行数值运算的轴类型(比如NumberAxis),两者的设计目标不匹配,进一步触发了类型转换错误。
解决方法
方法1:改用NumberAxis作为Y轴(推荐)
把Y轴换成NumberAxis,同时将数据的Y值改为Number类型。如果需要显示字符串标签,可以通过轴的格式化器将数值转换为想要的字符串:
LineChart<String, Number> createLineChart(){ CategoryAxis xAxis = new CategoryAxis(); NumberAxis yAxis = new NumberAxis(); // 设置Y轴标签格式化,把数值转成自定义字符串 yAxis.setTickLabelFormatter(new StringConverter<Number>() { @Override public String toString(Number object) { return "Y string " + object.intValue(); } @Override public Number fromString(String string) { return Integer.parseInt(string.replace("Y string ", "")); } }); LineChart<String, Number> lineChart = new LineChart<>(xAxis, yAxis); lineChart.getData().add(new XYChart.Series<>()); for (int i = 0; i < 3; i++) { lineChart.getData().get(0).getData().add(new XYChart.Data<>("X string %d".formatted(i+1), i+1)); } lineChart.getXAxis().setTickLabelFont(new Font(20)); lineChart.getYAxis().setTickLabelFont(new Font(20)); return lineChart; }
方法2:关闭LineChart的动画(不推荐)
如果一定要坚持用String类型数据,可以关闭LineChart的动画,避免触发内部的数值计算:
lineChart.setAnimated(false);
但这种方法只是规避了错误,并不是真正解决问题,而且会失去折线图的动画效果,不建议使用。
内容的提问来源于stack exchange,提问作者Keen Teen
相关产品推荐
相关产品推荐

