Generics 如何在JavaFX8中泛化TableView的FilteredList的谓词创建?

Generics 如何在JavaFX8中泛化TableView的FilteredList的谓词创建?,generics,javafx,tableview,javafx-8,Generics,Javafx,Tableview,Javafx 8,一位经验丰富的程序员提出的问题,他对Java还比较陌生 我创建了一个类的原型,该类将类似Excel的数据过滤器添加到TableView中的列中。我现在将它变成泛型的,这样它就可以与任何TableView一起工作,并且我一直在研究如何泛型谓词的创建 在我的原型中,我硬编码了所有内容,例如,我为字符串值列调用createPredicate方法,如下所示: createPredicate(TableViewModel::getTvValue); private void createPredicat

一位经验丰富的程序员提出的问题,他对Java还比较陌生

我创建了一个类的原型,该类将类似Excel的数据过滤器添加到TableView中的列中。我现在将它变成泛型的,这样它就可以与任何TableView一起工作,并且我一直在研究如何泛型谓词的创建

在我的原型中,我硬编码了所有内容,例如,我为字符串值列调用createPredicate方法,如下所示:

createPredicate(TableViewModel::getTvValue);
private void createPredicate(Function<TableViewModel, String> columnGetter) {
    //...
    final String filter = "some value";
    Predicate<TableViewModel> predicate = tvm -> columnGetter.apply(tvm).contains(filter);
    //...
}
在createPredicate方法中,我将谓词设置为:

createPredicate(TableViewModel::getTvValue);
private void createPredicate(Function<TableViewModel, String> columnGetter) {
    //...
    final String filter = "some value";
    Predicate<TableViewModel> predicate = tvm -> columnGetter.apply(tvm).contains(filter);
    //...
}
。。。第二,如何使包含泛型。我当前遇到找不到符号方法containsT编译器错误

private void createPredicate(Function<S, T> columnGetter) {

    final T filter = (T) "some value";
    //STUCK HERE:  How to genericise the contains?
    Predicate<S> predicate = tvm -> columnGetter.apply(tvm).contains(filter);

}
提前谢谢。如果有帮助,这里有一个MVCE。我使用的是JavaFX8JDK1.8.0µ和NetBeans8.2

光秃秃的骨头!TestColumnFilter.java类:

Test55.java,一个使用TestColumnFilter.java的虚拟类

import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Test55 extends Application {

    private Parent createContent() {

        TableView<TableViewModel> table = new TableView<>();
        TableColumn<TableViewModel, String> valueCol = new TableColumn<>("Value");
        valueCol.setCellValueFactory(cb -> cb.getValue().tvValueProperty());

        table.getColumns().addAll(Arrays.asList(valueCol));

        TestColumnFilter.createColumnFiltersForTableView(table);

        BorderPane content = new BorderPane(table);

        return content;
    }

    private class TableViewModel {

        private final StringProperty tvValue;

        public TableViewModel(
            String tvValue
        ) {
            this.tvValue = new SimpleStringProperty(tvValue);
        }

        public String getTvValue() {return tvValue.get().trim();}
        public void setTvValue(String tvValue) {this.tvValue.set(tvValue);}
        public StringProperty tvValueProperty() {return tvValue;}

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

我认为目前的设计最终不会成功

我假设您可以在多个列上设置过滤器,TableView应该显示基于所有过滤器的结果

您可以尝试以下方法:

public class TableViewFilter<S> {
    private final TableView<S> tableView;
    private final FilteredList<S> filteredItems;
    private final ObservableList<Function<S, Boolean>> conditions;

    public TableViewFilter(TableView<S> tableView, ObservableList<S> items) {
        this.tableView = tableView;
        this.filteredItems = new FilteredList<>(items);
        this.tableView.setItems(filteredItems ).

        this.conditions = FXCollections.observableArrayList();

        this.filteredItems.predicateProperty().bind(
            Bindings.createObjectBinding(this::generatePredicate, this.conditions));
    }

    public static <S> TableViewFilter<S> forTableView(TableView<S> tableView) {
        return new TableViewFilter<>(tableView);
    }

    public void addCondition(Function<S, Boolean> condition) {
        conditions.add(condition);
    }

    private Predicate<S> generatePredicate() {
        return item -> {
            return conditions.stream().map(func -> func.apply(item)).allMatch(Boolean.TRUE::equals);
        };
    }
}

TableView<TableViewModel> table = new TableView<>();
    TableColumn<TableViewModel, String> valueCol = new TableColumn<>("Value");
    valueCol.setCellValueFactory(cb -> cb.getValue().tvValueProperty());

    table.getColumns().addAll(Arrays.asList(valueCol));

    ObservableList<TableViewModel> items = FXCollections.observableArrayList();
    items.addAll(/*items*/);

    //TestColumnFilter.createColumnFiltersForTableView(table);
    TableViewFilter filter = TableViewFilter.forTableView(table, items); // Keep a reference if you need to add more conditions later
    filter.addCondition(item -> item.getTvValue().contains("filtered string"));

    BorderPane content = new BorderPane(table);

    return content;
}

请注意,该条件不依赖于特定的TableColumn。使用基础数据(即TableViewModel)进行过滤更容易。

您好,Jai,非常感谢您的回复。是的,您的假设是正确的–可以筛选多个列,并且每个列可能有多个筛选值。TableView将显示所有过滤器的结果。我可以看到你的代码是如何工作的,我会试一试。不过,请给我几天时间来使用它,因为我不熟悉您使用的一些语法,还需要做一些研究。一个问题:有没有一种方法可以获得TableColumn的getter?我试着得到它的细胞价值工厂,但不知道从那里去哪里。干杯@GreenZebra您的方法很奇怪,因为这里的每个TableColumn只表示表的模式。为特定列设置单元格工厂时,您告诉该列如何获取表中每一行的值。由于单元工厂用于确定单元的值,因此不可能将其反转以检索整个列中通用的getter函数。我现在明白了为什么我的方法很奇怪,效果不好。所以,我决定从头开始!我会尝试你的方法。我会看看我是怎么做的。谢谢你的提示。干杯,你好,洁。只是让你知道我能让你的代码正常工作。我更改了这个.tableView.setItemsitems。到this.tableView.setItemsfilteredItems;成功地过滤了一列。现在让它为多个列工作,每个列都有多个值。再次干杯@绿斑马很高兴知道它起作用了。这应该适用于多种情况。请注意,我的示例使用AND逻辑连接所有条件,即每一行必须满足所有条件,才能通过Stream::allMatch过滤掉。如果希望在满足任何条件时过滤掉条目,则可以将其更改为Stream::anyMatch。