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

为了进行演示,下面是我在JavaFX/OpenJFXTableView的Javadoc中看到的代码示例的修改版本.请注意getColumns行.

public class HelloApplication extends Application
{
    @Override
    public void start ( Stage stage )
    {
        TableView < Person > tableViewPersons = new TableView <> ( );
        ObservableList < Person > persons = FXCollections.observableArrayList ( this.fetchPersons ( ) );
        tableViewPersons.setItems ( persons );

        TableColumn < Person, String > firstNameCol = new TableColumn <> ( "First Name" );
        firstNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).firstNameProperty ( ).getName ( ) ) );

        TableColumn < Person, String > lastNameCol = new TableColumn <> ( "Last Name" );
        lastNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).lastNameProperty ( ).getName ( ) ) );

        tableViewPersons.getColumns ( ).setAll ( firstNameCol , lastNameCol );  // <---- 警告:为varargs参数创建未选中的泛型array.

        Scene scene = new Scene ( tableViewPersons , 320 , 240 );
        stage.setTitle ( "JavaFX Example" );
        stage.setScene ( scene );
        stage.show ( );
    }
…

台词是:

tableViewPersons.getColumns ( ).setAll ( firstNameCol , lastNameCol );

…为以下各项生成警告:

警告:为varargs参数创建未选中的泛型数组

我从this AnswerJava unchecked: unchecked generic array creation for varargs parameterthis post了解到,这个问题涉及到传递的参数类型的模棱两可.

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

        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.
        tableViewPersons.getColumns ( ).setAll ( columns );

请参阅完整上下文中的新代码:

public class HelloApplication extends Application
{
    @Override
    public void start ( Stage stage )
    {
        TableView < Person > tableViewPersons = new TableView <> ( );
        ObservableList < Person > persons = FXCollections.observableArrayList ( this.fetchPersons ( ) );
        tableViewPersons.setItems ( persons );

        TableColumn < Person, String > firstNameCol = new TableColumn <> ( "First Name" );
        firstNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).firstNameProperty ( ).getName ( ) ) );

        TableColumn < Person, String > lastNameCol = new TableColumn <> ( "Last Name" );
        lastNameCol.setCellValueFactory ( new PropertyValueFactory <> ( persons.get ( 0 ).lastNameProperty ( ).getName ( ) ) );

        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.
        tableViewPersons.getColumns ( ).setAll ( columns );

        Scene scene = new Scene ( tableViewPersons , 320 , 240 );
        stage.setTitle ( "JavaFX Example" );
        stage.setScene ( scene );
        stage.show ( );
    }
…

我的问题是:

  • 这是解决"未判断的泛型"警告的有效、完整的解决方案吗?
  • 有没有其他更简单的方法来解决"未判断的泛型"警告?(我不想取消这个警告.)

推荐答案

Using alternate API without varargs

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

例如,您可以在列表上调用clear,然后为每一项分别调用add方法,而不是调用setAll.键入setAll的varargs在很大程度上是执行此操作的一种方便方法.

这两个选项在含义上略有不同.因为该列表是可观察的,所以在每次原子操作之后都会触发更改.setAll使调用的所有更改都是原子的,因此触发的更改更少,但我认为在这种情况下这不会是一个问题.

如果您有许多列,则分别为每一列调用Add会更加繁琐,但会避免任何潜在的类型错误和警告.(我不是在提倡这种方法,只是让你知道这种可能性).

您可以替换呼叫:

tableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);  // <---- Warning: Unchecked generics array creation for varargs parameter.

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

tableViewPersons.getColumns().clear();
tableViewPersons.getColumns().add(firstNameCol);
tableViewPersons.getColumns().add(lastNameCol);

Your solution is OK (and also uses alternate API without varargs)

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

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.
tableViewPersons.getColumns ( ).setAll ( columns );

ObservableList重载了setAll个方法:

您将用单参数变量替换varargs API调用.这是一个完全有效的解决方案.对于不像这样提供重载接口的不同API,它在一般情况下不起作用,但在这种情况下它可以起作用.

在您的示例中,所有列都映射String个属性,我不会使用通配符类型?,而是使用:

List < TableColumn < Person, String > > columns = List.of ( firstNameCol , lastNameCol );

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

Using SafeVarargs

处理这种情况的另一种方法是创建一个helper方法,将其注释为SafeVarargs,以断言您正在安全地使用变量参数.

//...
addColumns(tableViewPersons, firstNameCol, lastNameCol);
//...

@SafeVarargs
private static void addColumns(
    TableView<Person> tableViewPersons, 
    TableColumn<Person, String>... columns) 
{
    tableViewPersons.getColumns().setAll(columns);
}

Unsolicited opinion on suppressing warnings

我知道您不想隐藏问题中提到的警告(这就是为什么我提供了其他选项).但我认为,在这种情况下,压制是可以的.

我的观点是,在许多情况下,未经判断的东西只是一种恼人的东西.泛型被添加到Java中,我认为即使是设计者也会说这是一种折衷的设计,因为语言最初并不是为泛型而构建的.在这种情况下,出现在一些未经判断的泛型警告周围的东西是折衷设计的一部分.

在TableView定义、支持列表等上拥有Main类型是好的(也是重要的).但是,当您开始不得不在使用已经类型化的类和方法时使用通配符类型或方法级别类型说明符,或者试图四处寻找以删除有关您已经know是安全的方法使用的警告时,这变得更加分散注意力,而不是有益.在这种情况下,我建议有 Select 地 Select suppressing the warnings个.

潜在地,您可以使用您的IDE来帮助消除不必要的警告,例如suppressing in Idea.在这样做之前,请判断它是否真的有可能出错,但如果不是(就像许多未经判断的泛型警告一样),那么我认为可以取消它们.

例如,要在IDEA IDE中取消对特定行的警告,可以使用以下注释(这是我通常所做的):

//noinspection unchecked
tableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);

遗憾的是,在一般情况下,Java只允许隐藏类、字段或方法级别的警告.

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

@SuppressWarnings("unchecked")
private TableView<Person> createPersonTableView(ObservableList<Person> people) {
    TableView<Person> tableViewPersons = new TableView<>(people);

    TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
    firstNameCol.setCellValueFactory(data -> data.getValue().firstNameProperty());

    TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
    firstNameCol.setCellValueFactory(data -> data.getValue().lastNameProperty());
    
    tableViewPersons.getColumns().setAll(firstNameCol, lastNameCol);
    
    return tableViewPersons;
}

Using lambda's instead of PropertyValueFactory

顺便提一句,但与泛型问题无关的是,上面的示例片段还说明了"正确"地使用lambdas来设置单元格值工厂,而不是依赖于PropertyValueFactory,如中所讨论的:

在我看来,使用lambdas而不是PropertyValueFactory比 suppress TableView方法调用上的varargs类型判断的问题更重要,因为在使用PropertyValueFactory时创建运行时错误要容易得多,而如果出现错误则更难进行调试.

Java相关问答推荐

缩小画布比例后更改滚动窗格的内部大小

使用标记时,场景大纲不在多个线程上运行

上下文初始化期间遇到异常-使用Java配置配置HibernateTemplate Bean时

在模拟超类中设置非setter属性的值

关于泛型的覆盖规则

Spring Boot Maven包

蒙蒂霍尔比赛结果不正确

如何在Java中从XML中获取特定的 node ,然后将其删除?

为什么在maven中,getLast方法不适用于List?

与Spring Boot相关的实体未正确保存

Spring Security不允许加载js

Jenv-相同的Java版本,但带有前缀

在Java 15应用程序中运行Java脚本和Python代码

如何在SWT菜单项文本中保留@字符

我们可以在方法中声明接口吗?

如何在Spring Boot中为不同的部署环境管理多个.properties文件?

多线程、并发和睡眠未按预期工作

如何用Micrometer&;斯普肯

Spring Boot Security-每个端点都被403禁止,Spring记录一个BasicErrorController#错误(HttpServlet请求)

Maven创建带有特定类的Spring Boot jar和普通jar