ClassCastException in JavaFX

Jin Lee :

My code gets the following error.

Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: javafx.scene.Group cannot be cast to javafx.scene.control.TreeCell

Source code

private TreeItem getClickedTreeItem(EventTarget eventTarget){
    TreeItem clickedTreeItem = null;

    if(eventTarget instanceof TreeCellSkin){
        clickedTreeItem = (TreeItem) ((TreeCell) ((TreeCellSkin)eventTarget).getParent()).getTreeItem();
    }else if(eventTarget instanceof LabeledText){
        clickedTreeItem = (TreeItem) ((TreeCell) ((LabeledText)eventTarget).getParent().getParent()).getTreeItem();
    }else if(eventTarget instanceof ImageView){
        clickedTreeItem = (TreeItem) ((TreeCell) ((ImageView)eventTarget).getParent().getParent()).getTreeItem();
    }

    return clickedTreeItem;
}

The console says this line:

clickedTreeItem = (TreeItem) ((TreeCell) ((ImageView)eventTarget).getParent().getParent()).getTreeItem();

This is legacy code that worked with Java 6, but gives the above exception using Java8?

What could be causing the ClassCastException now, and how to fix it for Java8?

Slaw :

The current code is brittle as it relies on the internal structure of the TreeCell. Also, TreeCellSkin1 and LabeledText are both internal classes. Internal code is subject to change without notice and without regards to third party reliance on it. Since this worked in Java 6 but not Java 8 I can only assume that the ImageView's grandparent changed from being the TreeCell to being a Group between the two versions.

To fix this you could look into the implementation and see what you need to do so you reach the TreeCell again, but that wouldn't really solve the problem. The use of EventTarget says to me this code was implemented while not fully understanding how event handling works in JavaFX. From the apparent goal of this code you should be using the source of the event, not the target. In JavaFX, the source of the event is always the object for which the EventHandler currently handling said Event was added to2. In other words, if you added the EventHandler to the TreeCell then the source will be the TreeCell. Using the source, and assuming EventHandler is added to the TreeCell, you can simply do:

TreeItem<?> item = ((TreeCell<?>) event.getSource()).getTreeItem();

Of course, if you're adding the EventHandler to the TreeCell you likely needn't bother with the source as you'll have access to the TreeCell directly. For example:

TreeView<String> treeView = new TreeView<>();
treeView.setCellFactory(tv -> {
    TreeCell<String> cell = new TreeCell<>(); // or some custom implementation
    cell.setOnMouseClicked(event -> {
        TreeItem<String> item = cell.getTreeItem();
        // do something with item...
    });
    return cell;
});

1. TreeCellSkin became public API in JavaFX 9 along with many (all?) skin implementations. They are part of the javafx.scene.control.skin package.
2. There's more to it but that's beyond the scope of this answer.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=94243&siteId=1