为什么编译器在使用 TableColumn<S, T> 而不是 TableColumn<S, ?> 到 select JavaFX8 TableView 中的单元格时生成未经检查的警告?

Why does the compiler generate unchecked warnings when using TableColumn<S, T> rather than TableColumn<S, ?> to select a cell in a JavaFX8 TableView?

亲戚Java菜鸟问题

我正在清理我的代码,遇到了两个未经检查的警告(“未经检查的方法调用”和“未经检查的转换”)在以编程方式选择 TableView 中的单元格时。我能够解决这些警告,但不明白是什么原因造成的。

这就是我最初编码 select():

的方式
TablePosition<?, ?> previousPos;
//...
table.getSelectionModel().select
    (previousPos.getRow(), previousPos.getTableColumn());

我通过将其更改为此解决了警告。

table.getSelectionModel().select
    (previousPos.getRow(), table.getColumns().get(previousPos.getColumn()));

我不明白其中的区别,所以我查看了 Java 源代码。如果我解释正确,TableView.java 中的 select() 方法需要 TableColumn<S, ?>。但是,getTableColumn()TablePosition.java returns TableColumn<S,T>getColumns()TableView.java returns 类型 TableColumn(S, ?)ObservableList

我想这就是 table.getColumns().get(...) 编译干净而 previousPos.getTableColumn() 产生错误的原因。

但是,从编译器的角度来看,TableColumn<S, ?>TableColumn<S,T> 之间有什么区别呢?为什么它不能将 T 解析为 ?.

(这是正确的术语吗?)

如果有帮助,这是我用来尝试找出答案的 MVCE,但答案超出了我目前的 Java 知识范围。 select()moveToPreviousPos().

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

package test27;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Test27 extends Application {

    TableView<TestModel> table = new TableView<>();

    //Both of these stop the "unchecked conversion" error on line #51 (previousPos = table.getFocusModel().getFocusedCell();)
    //    TablePosition previousPos;
        TablePosition<?, ?> previousPos;
    //but using the following generates the "unchecked conversion" error
    //    TablePosition<TestModel, ?> previousPos;

    private Parent createContent() {

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

        olTestModel.add(new TestModel("A"));
        olTestModel.add(new TestModel("B"));
        olTestModel.add(new TestModel("C"));

        table.setItems(olTestModel);

        TableColumn<TestModel, String> colText = new TableColumn<>("text");
        colText.setCellValueFactory(cellData -> cellData.getValue().textProperty());
        colText.setCellFactory(TextFieldTableCell.forTableColumn());

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

        Button btnGetTablePosition = new Button("get table position");
        btnGetTablePosition.setOnAction(event -> {
            //TableView.java:  getFocusedCell returns TablePosition<S, ?>
            previousPos = table.getFocusModel().getFocusedCell(); //Line #51
            outputPreviousPos(previousPos);
        });

        Button btnMoveToPreviousPos = new Button("move to previous pos");
        btnMoveToPreviousPos.setOnAction(event -> {
            moveToPreviousPos(previousPos);
        });

        BorderPane content = new BorderPane(table);
        HBox hb = new HBox();
        hb.getChildren().addAll(btnGetTablePosition, btnMoveToPreviousPos);

        content.setTop(hb);

        return content;

    }

    public void outputPreviousPos(TablePosition previousPos){

        System.out.println("previousPos = " + previousPos);

    }

    public void moveToPreviousPos(TablePosition previousPos) {

        //select() in TableView.java expects TableColumn<S, ?>
        //getTableColumn() in TablePosition.java returns TableColumn<S,T>
        //getColumns() in TableView.java returns an ObservableList of type TableColumn(S, ?)

        //Is that why the following line generates  "unchecked method invocation" and "unchecked conversion" errors
        //table.getSelectionModel().select
        //      (previousPos.getRow(), previousPos.getTableColumn());
        //but the following line compiles clean?
        table.getSelectionModel().select
                (previousPos.getRow(), table.getColumns().get(previousPos.getColumn()));        

    }

    public class TestModel {

        private StringProperty text;

        public TestModel() {
            this("");
        }

        public TestModel(
            String text
        ) {
            this.text = new SimpleStringProperty(text);
        }

        public String getText() {
            return text.get();
        }

        public void setText(String text) {
            this.text.set(text);
        }

        public StringProperty textProperty() {
            return text;
        }

    }

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

}

首先,我想说 TableView.getFocusModel().getFocusedCell()(或 focusedCellProperty())使用原始 TablePosition 类型是一个设计缺陷(原始 TablePosition 只能转换为 TablePositon<?, ?> 而不会产生警告)。更合适的版本可能是 TablePosition<S, ?>.

由于 TablePosition 表示 TableView 任意位置 的特定单元格,因此对于 TableView.getFocusModel().focusedCellProperty()

  1. S 的类型固定用于 TableView<S>(即类型已知)。
  2. T 的类型指的是特定 TableColumn 的类型,它可以根据焦点所在的单元格而改变。您无法在编译时确定类型,因为您不知道将选择哪个 TableColumn<S, T>

现在进入第二点。

But what is the difference, then, between TableColumn<S, ?> and TableColumn<S,T> from the compiler's point of view? Why doesn't it resolve (is that the correct term?) the T to ?.

这是因为TableColumn<S, ?>中的?是通配符。由于 TableViewFocusModel<S> 没有类型 T 并且它不基于固定列进行操作,所以它必须在这里使用 ? (见我解释的第一点)。

另一方面,TableView.getSelectionModel().select() 期望 rowint 和列的 TableColumn<S, ?>。因为你有 TablePosition<?, ?> previousPos,所以 previousPos 也会 return 和 TableColumn<?, ?>TableColumn<S, ?TableColumn<?, ?> 非常不同 - 如果允许 TableColumn<?, ?>,您可以使用另一个 TableViewTableColumn

最后,如果TableView.getFocusModel().getFocusedCell()的API是固定的,那么使用select()就不会有问题了。在那之前,您可以手动将其强制转换为 TablePosition<TestModel, ?>,或者像您所做的那样使用 TableView.getColumns().get(index)