在 JavaFX 中的 TableView 上设置列时如何解决“未检查的泛型”警告

Bas*_*que 4 java javafx tableview

在向JavaFX应用程序中的表视图控件添加列的常见情况下,我遇到了编译器警告。

\n

为了进行演示,下面是我在 JavaFX/ OpenJFX类的 Javadoc 中看到的代码示例的修改版本TableView。注意该getColumns线。

\n
public class HelloApplication extends Application\n{\n    @Override\n    public void start ( Stage stage )\n    {\n        TableView < Person > tableViewPersons = new TableView <> ( );\n        ObservableList < Person > persons = FXCollections.observableArrayList ( this.fetchPersons ( ) );\n        tableViewPersons.setItems ( persons );\n\n        TableColumn < Person, String > firstNameCol = new TableColumn <> ( "First Name" );\n        firstNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).firstNameProperty ( ).getName ( ) ) );\n\n        TableColumn < Person, String > lastNameCol = new TableColumn <> ( "Last Name" );\n        lastNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).lastNameProperty ( ).getName ( ) ) );\n\n        tableViewPersons.getColumns ( ).setAll ( firstNameCol , lastNameCol );  // <---- Warning: Unchecked generics array creation for varargs parameter.\n\n        Scene scene = new Scene ( tableViewPersons , 320 , 240 );\n        stage.setTitle ( "JavaFX Example" );\n        stage.setScene ( scene );\n        stage.show ( );\n    }\n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

该行:

\n
tableViewPersons.getColumns ( ).setAll ( firstNameCol , lastNameCol );\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 生成警告:

\n
\n

警告:未检查 varargs 参数的泛型数组创建

\n
\n

我从这个 Answer on Java unchecked: unchecked generic array Creation for varargsparameter这篇文章中了解到,该问题涉及传递的参数类型的歧义。

\n

如果我的目标是解决此编译器警告,那么一种解决方案是明确TableColumn传递给 的对象集合的参数化类型ObservableList#setAll。所以我们可以显式声明一个List对象TableColumn

\n
tableViewPersons.getColumns ( ).setAll ( firstNameCol , lastNameCol );\n
Run Code Online (Sandbox Code Playgroud)\n

在完整的上下文中查看新代码:

\n
        List < TableColumn < Person, ? > > columns = List.of ( firstNameCol , lastNameCol );  // <--- Adding this line to make explicit the parameterized type of `TableColumn` to resolve the "Unchecked generics" warning.\n        tableViewPersons.getColumns ( ).setAll ( columns );\n
Run Code Online (Sandbox Code Playgroud)\n

我的问题:

\n
    \n
  • 这是解决“未经检查的泛型”警告的有效、完整的解决方案吗?
  • \n
  • 还有其他更简单的方法来解决“未检查的泛型”警告吗?(我宁愿不压制警告。)
  • \n
\n

jew*_*sea 6

使用不带可变参数的备用 API

\n

处理未经检查的泛型警告(并非在所有情况下都有效)的一种方法是重写代码以使用不易受到此类问题影响的不同 API。

\n

例如,setAll您可以调用clear列表,然后add为每个项目单独调用该方法,而不是调用 。输入的可变参数setAll主要是执行此操作的便捷方法。

\n

这两个选项的含义略有不同。由于列表是可观察的,因此每次原子操作后都会触发更改。setAll使调用的所有更改都是原子的,从而导致触发的更改更少,但我认为在这种情况下这不会成为问题。

\n

如果您有很多列,则为每个列单独调用 add 会更冗长,但会避免任何潜在的类型错误和警告。(我并不是提倡这种方法,只是让你知道这种可能性)。

\n

您可以替换呼叫:

\n
tableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);  // <---- Warning: Unchecked generics array creation for varargs parameter.\n
Run Code Online (Sandbox Code Playgroud)\n

对于不使用 varargs 参数的备用 API 调用:

\n
tableViewPersons.getColumns().clear();\ntableViewPersons.getColumns().add(firstNameCol);\ntableViewPersons.getColumns().add(lastNameCol);\n
Run Code Online (Sandbox Code Playgroud)\n

您的解决方案没问题(并且还使用不带可变参数的备用 API)

\n

解决此问题的方法是预先创建给定类型的列表并将其提供给 TableView,这与上一节中描述的方法实际上相同,您使用的是不同的 API,而不是 varargs API。

\n
\n
List < TableColumn < Person, ? > > columns = List.of ( firstNameCol , lastNameCol );  // <--- Adding this line to make explicit the parameterized type of `TableColumn` to resolve the "Unchecked generics" warning.\ntableViewPersons.getColumns ( ).setAll ( columns );\n
Run Code Online (Sandbox Code Playgroud)\n
\n

ObservableList有重载setAll方法:

\n\n

您正在将 varargs API 调用替换为单参数变体。这是一个完全有效的解决方案。对于不提供此类重载接口的不同 API,它在一般情况下无法工作,但在本例中它可以工作。

\n

在您的情况下,所有列都是映射String属性,我不会使用通配符 type ?,而是使用:

\n
List < TableColumn < Person, String > > columns = List.of ( firstNameCol , lastNameCol );\n
Run Code Online (Sandbox Code Playgroud)\n

并且仅当每列映射到不同类型时才使用通配符类型。

\n

使用SafeVarargs

\n

处理此问题的另一种方法是创建一个辅助方法,并进行注释SafeVarargs以断言您正在安全地使用变量参数。

\n
//...\naddColumns(tableViewPersons, firstNameCol, lastNameCol);\n//...\n\n@SafeVarargs\nprivate static void addColumns(\n    TableView<Person> tableViewPersons, \n    TableColumn<Person, String>... columns) \n{\n    tableViewPersons.getColumns().setAll(columns);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

关于压制警告的主动意见

\n

我知道您不想抑制问题中提到的警告(这就是我提供其他选项的原因)。但我认为,在这种情况下,压制是可以的。

\n

我的观点是,在很多情况下,未经检查的东西只是一种烦恼。泛型被添加到 Java 中,我认为即使是设计者也会说这是一种妥协的设计,因为该语言最初并不是为泛型而构建的。在这种情况下,围绕一些未经检查的泛型警告出现的内容是妥协设计的一部分。

\n

在 TableView 定义、支持列表等中包含主要类型是很好的(而且很重要)。但是,当您开始必须在使用已经键入的类和方法时使用通配符类型或方法级别类型说明符,或者尝试四处寻找以删除您已经知道是安全的方法使用的警告时它变得更加困难分散注意力而不是有益。在这种情况下,我建议有选择地抑制警告。

\n

您可以使用 IDE 来帮助抑制不必要的警告,例如在 Idea 中抑制。在这样做之前,请检查它是否确实是一个重大的潜在错误,但如果不是(就像许多未经检查的泛型警告 IMO),那么我认为抑制它们是可以的。

\n

例如,要抑制 Idea IDE 中特定行的警告,可以使用以下注释(这是我通常所做的):

\n
//noinspection unchecked\ntableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);\n
Run Code Online (Sandbox Code Playgroud)\n

不幸的是,在一般情况下,Java 只允许在类、字段或方法级别上抑制警告。

\n

因此,在方法级别执行此操作的一种可移植方法是使用注释,SupressWarnings将有限的相关代码放置在一个小方法中:

\n
@SuppressWarnings("unchecked")\nprivate TableView<Person> createPersonTableView(ObservableList<Person> people) {\n    TableView<Person> tableViewPersons = new TableView<>(people);\n\n    TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");\n    firstNameCol.setCellValueFactory(data -> data.getValue().firstNameProperty());\n\n    TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");\n    firstNameCol.setCellValueFactory(data -> data.getValue().lastNameProperty());\n    \n    tableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);\n    \n    return tableViewPersons;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用 lambda 代替 PropertyValueFactory

\n

顺便说一句,但与泛型问题无关,上面的示例片段还说明了如何“正确”使用 lambda 来设置单元格值工厂,而不是依赖于PropertyValueFactory,如下所述:

\n\n

在我看来,使用 lambda 代替比PropertyValueFactory抑制 TableView 方法调用上的可变参数类型检查的问题更重要,因为使用 a 时更容易创建运行时错误,PropertyValueFactory并且如果确实出现错误,则更难以调试它们。

\n