如何从 JavaFX8 中的 TableView 焦点模型获取 <S, T> 类型的 TableColumn 并仍然获得干净的编译?

How to get a TableColumn of type <S, T> from a TableView's focus model in JavaFX8 and still get a clean compile?

又一个亲戚Java菜鸟问题

我正在清理我的代码,正在处理最后两个编译器问题。两者都涉及需要从对 tableView.getFocusModel().getFocusedCell() 的调用中获取 <S, T> 类型的 TableColumn。我收到编译器警告,但尚未能够解决它们。

我认为这些警告源于 TableView 的焦点模型对原始 TablePosition 类型的使用,如果我理解正确的话,这意味着任何 TableColumn 基于调用 tableView.getFocusModel().getFocusedCell() 返回的 TablePosition has 声明为 raw 类型,以便干净编译的代码。

即。如果我这样做:

TablePosition pos = tableView.getFocusModel().getFocusedCell();

那么如果我这样做,我将永远得到一个干净的编译:

TableColumn col = pos.getTableColumn();

TablePosition.java 在 Java 文档中说 pos.getTableColumn() returns TableColumn<S,T> 我想它可以' 如果焦点模型返回原始 TablePosition 类型,则执行此操作。我明白了。

但是,如果我必须将 TableColumn 声明为原始的,我如何 "convert" 将其键入 <S, T>(或绕过泛型? ) 这样我最终得到 TableColumn<S, T> 并且仍然得到一个干净的编译 ?一旦我知道了这一点,我希望能够解决最后两个编译器问题。

我用 and while that works, suppressing compiler errors isn't what I want to do. I also tried using the TableView's selection model rather than the focus model but it seems that table.getSelectionModel().getSelectedCells().get(0) returns a raw TablePosition, too. The focus model's use of raw TablePosition was explained by user Jai in the reply to an earlier question I asked 中接受的答案进行了试验。

如果有帮助,这是我正在尝试解决最后两个编译器问题的 MVCE。两者都涉及需要在每个 getFocussedColumn()getGraphic() 方法中获得 <S, T> 类型的 TableColumn。我留下了我的评论,这样你就可以看到我试过的东西。此外,为了重现问题,MVCE 必须使用我的应用程序使用的自定义复选框单元格。我已经尽可能多地修剪它以尽可能保持它 "M"。

我正在使用 JavaFX8 (JDK1.8.0_181)、NetBeans 8.2 和 Scene Builder 8.3.

我正在使用“-Xlint:unchecked”进行编译。

package test28;

import java.util.function.BiConsumer;
import java.util.function.Function;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Test28 extends Application {

    static TableView<TestModel> table = new TableView<>();
    ToggleGroup tgRadios = new ToggleGroup();

    private Parent createContent() {

        ObservableList<TestModel> olTestModel = FXCollections.observableArrayList();

        olTestModel.add(new TestModel(false));
        olTestModel.add(new TestModel(true));
        olTestModel.add(new TestModel(false));

        table.setItems(olTestModel);
        Platform.runLater(() -> table.getSelectionModel().selectFirst());

        //Column with a custom cell that renders as a CheckBox
        TableColumn<TestModel, Boolean> colCheckbox = createCheckBoxColumn("checkbox", TestModel::checkboxProperty, TestModel::setCheckbox);

        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setEditable(true);
        table.getColumns().add(colCheckbox);

        Button btnGetFocussedCol = new Button("get focussed col");
        btnGetFocussedCol.setOnAction(event -> {
            TableColumn<TestModel, Boolean> col = getFocussedColumn();
            System.out.println("col = " + col);
        });

        Button btnGetGraphic = new Button("get graphic");
        btnGetGraphic.setOnAction(event -> {
            Node node = getGraphic();
            System.out.println("node = " + node);
        });

        BorderPane content = new BorderPane(table);
        HBox hb = new HBox();
        hb.setPadding(new Insets(10,10,10,10));
        hb.setSpacing(20.0);
        hb.getChildren().addAll(btnGetFocussedCol, btnGetGraphic);

        content.setTop(hb);

        return content;

    }

    //*************************************************************************************
    //First of two remaining compiler issues to resolve

    private static <S, T> TableColumn<S, T> getFocussedColumn() {

        //getFocusedCell() returns a RAW TablePosition
        TablePosition pos = table.getFocusModel().getFocusedCell();

        //pos.getTableColumn() should be returning TableColumn<S,T>, however, pos is a RAW type
        //so I guess that's why I'm getting an unchecked conversion warning on the following line
        //TableColumn<S, T> col = pos.getTableColumn();

        //Casting it generates an unchecked cast warning
        //TableColumn<S, T> col = (TableColumn<S, T>) pos.getTableColumn();

        //Using a RAW TableColumn type fixes the warnings on the TableColumn declaration
        //but generates an unchecked conversion warning on the "return col" line
        TableColumn col = pos.getTableColumn();

        //table.getColumns() returns ObservableList<TableColumn<S,?>>, so I cannot use the following
        //TableColumn<S, T> col = table.getColumns().get(pos.getColumn());

        //getFocusedCell().getTableColumn() returns TableColumn<S,?>, so I cannot do the following
        //return table.getFocusModel().getFocusedCell().getTableColumn();

        //unchecked conversion warning generated on this line when TableColumn is raw
        //==> HOW CAN I "CONVERT" A RAW TABLECOLUMN TO TABLECOLUMN<S, T>?
        return col;

    }

    //*************************************************************************************
    //Second of two remaining compiler issues to resolve

    public <S, T> Node getGraphic() {

        TablePosition pos = table.getFocusModel().getFocusedCell();

        //Declaring TableColumn as a raw type generates an "unchecked call to call(P) as a member of the raw type Callback"
        //warning on the TableCell declaration below
        TableColumn col = pos.getTableColumn();

        //getCellFactory().call() expects CellDataFeatures<S,T> and returns ObservableValue<T>
        //unchecked call to call(P) warning generated on this line when TableColumn is raw
        //==> HOW CAN I "CONVERT" A RAW TABLECOLUMN TO TABLECOLUMN<S, T>?
        TableCell cell = (TableCell) col.getCellFactory().call(col);

        Node cellGraphic = cell.getGraphic();
        return cellGraphic;

    }

    //*************************************************************************************
    //Create a custom checkbox

    public <S> TableColumn<S, Boolean> createCheckBoxColumn(String title, 
            Function<S, BooleanProperty> methodGetCheckBoxFieldProperty, 
            BiConsumer<? super S, ? super Boolean> methodSetCheckBoxField) {

        TableColumn<S, Boolean> col = new TableColumn<>(title);

        col.setCellValueFactory(cellData -> methodGetCheckBoxFieldProperty.apply(cellData.getValue()));

        col.setCellFactory(column -> CheckBoxCell.createCheckBoxCell());

        col.setOnEditCommit(event -> {
            Boolean masterCode = event.getNewValue();
            S dataModel = event.getRowValue();
            methodSetCheckBoxField.accept(dataModel, masterCode);
        });

        return col;

    }

    //*************************************************************************************
    //The custom CheckBoxCell class

    public static class CheckBoxCell<S, T> extends TableCell<S, T> {

        private final CheckBox checkBox = new CheckBox();

        public CheckBoxCell() {

            setGraphic(checkBox);
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        }

        public static <S> CheckBoxCell<S, Boolean> createCheckBoxCell() {
            return new CheckBoxCell<S, Boolean>();
        }

        @Override
        protected void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setGraphic(null);
            } else {
                setText( ((Boolean) item).toString());
                checkBox.setSelected( (Boolean) item);
                setGraphic(checkBox);
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            }
        }

    }    

    //*************************************************************************************

    public class TestModel {

        private BooleanProperty checkbox;

        public TestModel() {
            this(false);
        }

        public TestModel(
            boolean checkbox
        ) {
            this.checkbox = new SimpleBooleanProperty(checkbox);
        }

        public boolean getCheckbox() {
            return checkbox.get();
        }

        public void setCheckbox(boolean checkbox) {
            this.checkbox.set(checkbox);
        }

        public BooleanProperty checkboxProperty() {
            return checkbox;
        }

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.setWidth(500);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

感谢用户 Jai 的帮助,答案是从对 tableView.getFocusModel().getFocusedCell(); 的调用中获取 TableColumn<S, T> 并获得干净编译的唯一方法是使用 @SuppressWarnings("unchecked") 注释.

这是因为:

  1. TableView API 被写入 return 来自 getFocusedCell() 的原始 TablePosition 类型。
  2. 无法将原始类型 "convert" 转换为 <S, T>(或任何其他)类型。它们是非常不同的东西。

因此,我的问题中 MVCE 中突出显示的编译器问题是这样解决的:

@SuppressWarnings("unchecked") 
private static <S, T> TableColumn<S, T> getFocussedColumnx() {
    TablePosition<S, T> pos = (TablePosition<S, T>) table.getFocusModel().getFocusedCell();
    TableColumn<S, T> col = (TableColumn<S, T>) pos.getTableColumn();
    return col;
}

还有这个:

@SuppressWarnings("unchecked")
public <S, T> Node getGraphic() {
    TablePosition<S, T> pos = (TablePosition<S, T>) table.getFocusModel().getFocusedCell();
    TableColumn<S, T> col = (TableColumn<S, T>) pos.getTableColumn();
    TableCell<S, T> cell = (TableCell<S, T>) col.getCellFactory().call(col);
    Node cellGraphic = cell.getGraphic();
    return cellGraphic;
}

谢谢 Jai 为我指明了正确的方向。