JavaFX泛型在自定义使用者和每个使用者所需的不兼容类型之间

时间:2015-07-28 18:05:38

标签: java generics javafx-8

我对泛型有一些麻烦,尽管找到了解决方法,但我不明白是什么阻止了我的代码编译。

我有一个显示TreeTableView的JavaFX项目:

<?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>

树只显示一个对象数组“Date”,第一列包含字符串,第二列包含图像:

public enum DateType {
    WORKDAY("work.jpg"),
    HOLIDAY("holiday.jpg");

    private Image image;

    DateType(String imgPath) {
        image = new Image(FXMLController.class.getResourceAsStream(imgPath));
    }

    public Image getImage() {
        return image;
    }
}

public class Date {
    public LocalDate date;
    public DateType dayType;

    public String getDate() {
        return date.toString();
    }

    public ImageView getDateType() {
        return new ImageView(dayType.getImage());
    }
}

我有一个自定义的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()
            ));
        }
    }

}

编译错误由与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>());
    }    

}

方法“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>());

我不明白为什么同一方法的2个第一行根本没有提出任何东西,在功能上它们做同样的事情?

1 个答案:

答案 0 :(得分:1)

您的Consumer实例使用raw type当您使用原始类型时,编译器会忽略泛型类型检查,这就是为什么它不会在此行上抛出错误:< / p>

Consumer consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();

但是,如果您将上述行更改为此...

Consumer<TreeTableColumn<Date, ImageView>> consumer = new TreeTableColumnConfigurator<Date, DateType, ImageView>();

...并尝试以这种方式编译它,因为Consumer实例已被赋予类型参数,所以第二行会出现相同的错误。

为什么会出现错误? tree.getColumns().stream()会返回Stream<TreeTableColumn<Date, ?>>,而forEach会返回Consumer<TreeTableColumn<Date, ?>>。然而,值得注意的是,TreeTableColumn<Date, ?>TreeTableColumn<Date, ImageView> 不一样,就像List<Object>List<?>不一样;这就是编译器提到的Consumer<TreeTableColumn<Date, ImageView>>无法转换为Consumer<TreeTableColumn<Date, ?>>的原因。

虽然在这种情况下使用原始类型工作,但您应该提出一个适合您需要的通用解决方案(例如,保留通配符)(如果存在)。