从 LineChart<String,String> 中删除数据时出现特殊的 ClassCastException

Kee*_*een 4 java javafx

考虑以下应用:

在此输入图像描述

我希望每次单击按钮时,都会LineChart删除第一个数据。点击后的输出必须如下:

第一次点击后

在此输入图像描述

第二次点击后

在此输入图像描述

第三次点击后

在此输入图像描述

但是,第一次点击后,我得到以下信息ClassCastException

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)
    at javafx.base@18/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at javafx.base@18/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.base@18/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
    at javafx.base@18/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.base@18/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.base@18/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
    at javafx.base@18/javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:190)
    at com.example.demo/com.example.demo.MyApplication.lambda$start$0(MyApplication.java:21)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base@18/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@18/javafx.scene.Node.fireEvent(Node.java:8797)
    at javafx.controls@18/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls@18/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
    at javafx.controls@18/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@18/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@18/javafx.scene.Scene$MouseHandler.process(Scene.java:3881)
    at javafx.graphics@18/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics@18/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2607)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@18/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@18/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@18/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@18/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)
Run Code Online (Sandbox Code Playgroud)

代码如下:

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();
    }

}
Run Code Online (Sandbox Code Playgroud)

问题

当我没有类型数据时,为什么会在toClassCastException转换时引发?StringNumberNumber

Jam*_*s_D 5

这是实现中的一个错误LineChart我在https://bugs.openjdk.org/secure/Dashboard.jspa找不到它的有效错误报告。

问题出现在对方法的调用中createDataRemoveTimeline(...)(在当前实现中找到),该方法的实现如下

    private Timeline createDataRemoveTimeline(final Data<X,Y> item, final Node symbol, final Series<X,Y> series) {
        Timeline t = new Timeline();
        // save data values in case the same data item gets added immediately.
        XYValueMap.put(item, ((Number)item.getYValue()).doubleValue());

        t.getKeyFrames().addAll(new KeyFrame(Duration.ZERO, new KeyValue(item.currentYProperty(),
                item.getCurrentY()), new KeyValue(item.currentXProperty(),
                item.getCurrentX())),
                new KeyFrame(Duration.millis(500), actionEvent -> {
                    if (symbol != null) getPlotChildren().remove(symbol);
                    removeDataItemFromDisplay(series, item);
                    XYValueMap.clear();
                },
                new KeyValue(item.currentYProperty(),
                item.getYValue(), Interpolator.EASE_BOTH),
                new KeyValue(item.currentXProperty(),
                item.getXValue(), Interpolator.EASE_BOTH))
        );
        return t;
    }
Run Code Online (Sandbox Code Playgroud)

该实现会缓存被删除的值(据我所知,这是需要的,以防在删除动画正在进行时添加回相同的值)Map<Data<X,Y>, Double>。这假设 y 值是 a Number(请注意方法第二行中的转换),这当然不一定正确。我认为这应该用 a 来实现Map<Data<X,Y>, Y>,尽管我还没有完全分析代码。

createDataRemoveTimeline(...)方法从方法中调用dataItemRemoved(...)(当从数据系列之一中删除项目时调用),但仅当animated设置为 true 时。因此,一种解决方法是关闭动画:

lineChart.setAnimated(false);
Run Code Online (Sandbox Code Playgroud)