JavaFX泛型自定义使用者和必需的forEach使用者之间的不兼容类型

JavaFX泛型自定义使用者和必需的forEach使用者之间的不兼容类型,java,generics,javafx-8,Java,Generics,Javafx 8,我在泛型方面遇到了一些问题,尽管找到了解决方法,但我不明白是什么阻止了我的代码编译 我有一个JavaFX项目,它显示一个TreeTableView: <?xml version="1.0" encoding="UTF-8"?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?>

我在泛型方面遇到了一些问题,尽管找到了解决方法,但我不明白是什么阻止了我的代码编译

我有一个JavaFX项目,它显示一个TreeTableView:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication7.FXMLController">
   <children>
      <TreeTableView fx:id="tree" layoutX="43.0" layoutY="30.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <columns>
          <TreeTableColumn id="date" prefWidth="399.0" text="Date" />
          <TreeTableColumn id="dateType" minWidth="0.0" prefWidth="200.0" text="Type" />
        </columns>
      </TreeTableView>
   </children>
</AnchorPane>
我有一个自定义的Consumer类,用于执行所有TreeTableView配置:

import java.util.Comparator;
import java.util.function.Consumer;
import javafx.scene.Node;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;

public class TreeTableColumnConfigurator<S, E extends Enum<E>, N extends Node> implements Consumer<TreeTableColumn<S, N>> {

    @Override
    public void accept(TreeTableColumn<S, N> t) {
        t.setCellValueFactory(new TreeItemPropertyValueFactory(t.getId()));
        t.setCellFactory(column -> {
            TreeTableCell cell = TreeTableColumn.DEFAULT_CELL_FACTORY.call(column);
            return cell;
        });
        if ("dateType".equals(t.getId())) {
            t.setComparator(Comparator.<N, E>comparing(
                    node -> (E) node.getUserData()
            ));
        }
    }

}
import java.util.Comparator;
导入java.util.function.Consumer;
导入javafx.scene.Node;
导入javafx.scene.control.TreeTableCell;
导入javafx.scene.control.TreeTableColumn;
导入javafx.scene.control.cell.TreeItemPropertyValueFactory;
公共类TreeTableColumnConfigurator实现使用者{
@凌驾
公共void accept(树表t列){
t、 setCellValueFactory(新的TreeItemPropertyValueFactory(t.getId());
t、 setCellFactory(列->{
TreeTableCell cell=TreeTableColumn.DEFAULT\u cell\u FACTORY.call(column);
返回单元;
});
if(“dateType”.equals(t.getId())){
t、 设置比较器(Comparator.comparating(
node->(E)node.getUserData()
));
}
}
}
与FXML关联的控制器引发编译错误:

import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;
import java.util.function.Consumer;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeTableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class FXMLController implements Initializable {

    @FXML
    private TreeTableView<Date> tree;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        /* working workaround */
        Consumer consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();
        tree.getColumns().stream().forEach(consumer);
        /* not compiling code */
        tree.getColumns().stream().forEach(new TreeTableColumnConfigurator<Date, DateType, ImageView>());
    }    

}
import java.net.URL;
导入java.time.LocalDate;
导入java.util.ResourceBundle;
导入java.util.function.Consumer;
导入javafx.fxml.fxml;
导入javafx.fxml.Initializable;
导入javafx.scene.control.TreeTableView;
导入javafx.scene.image.image;
导入javafx.scene.image.ImageView;
公共类FXMLController实现可初始化{
@FXML
私有树视图树;
/**
*初始化控制器类。
*/
@凌驾
公共void初始化(URL、ResourceBundle rb){
/*工作变通办法*/
消费者消费者=新的TreeTableColumnConfigurator();
tree.getColumns().stream().forEach(使用者);
/*不编译代码*/
tree.getColumns().stream().forEach(新的TreeTableColumnConfigurator());
}    
}
方法“initialize”的最后一行引发以下错误:

FXMLController.java:32: error: incompatible types: TreeTableColumnConfigurator<Date,DateType,ImageView> cannot be converted to Consumer<? super TreeTableColumn<Date,?>>
        tree.getColumns().stream().forEach(new TreeTableColumnConfigurator<Date, DateType, ImageView>());
FXMLController.java:32:错误:不兼容的类型:TreeTableColumnConfigurator无法转换为使用者>
tree.getColumns().stream().forEach(新的TreeTableColumnConfigurator());

我不明白为什么同一方法的前两行在功能上都是一样的,根本没有引发任何问题?

您的
使用者
实例使用了一个当您使用原始类型时,编译器忽略泛型类型检查,这就是为什么它不会在这一行抛出错误:

Consumer consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();
Consumer=new treetablecolumn配置器();
但是,如果您将上面的行更改为此

Consumer<TreeTableColumn<Date, ImageView>> consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();
Consumer=new treetablecolumn配置器();
…并尝试以这种方式编译它,第二行中出现的错误将与您在
Consumer
实例中得到的类型参数相同

为什么会出现错误
tree.getColumns().stream()
返回
,而
forEach
需要
消费者
。然而,需要注意的是,
TreeTableColumn
TreeTableColumn
是不一样的,就像
List
List
是不一样的;这就是为什么正如编译器所提到的,
Consumer
不能转换为
Consumer


虽然在这种情况下使用原始类型是可行的,但是如果存在通配符,您应该找到适合您需要的通用解决方案(例如,保留通配符)

感谢您对原始类型的解释。看起来TreeTableColumn与TreeTableColumn不兼容,因为Java中的泛型不协变,而dotNet中的泛型可以协变。但更重要的是,我不知道我能做些什么来只使用参数化类型并保留通配符。。。我可以通过将configurator类更改为“class TreeTableColumnConfigurator implements Consumer”来部分实现它,但是我不能再使用泛型实现dateType列的Comparator…经过一些实践,我了解Java可以实现协变泛型,但是如果我必须修改一些通配符泛型,我将被迫在某些时候使用等效的原始类型,否则编译器总会在某些地方抱怨。只有以只读方式处理通配符泛型时,一切都很好。
Consumer<TreeTableColumn<Date, ImageView>> consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();