简体   繁体   中英

Java Generics incompatible types in Supplier accept method

I have a method with generic parameters:

public static void addActionColumnAndSetSelectionListener(Grid<? extends UpdatableRecord<?>> grid,
                                                              EditDialog<? extends UpdatableRecord<?>> dialog,
                                                              Callback afterSave, Supplier<UpdatableRecord<?>> onNewRecord,
                                                              Consumer<? extends UpdatableRecord<?>> insteadOfDelete) {
        Button buttonAdd = new Button(grid.getTranslation("Add"));
        buttonAdd.addClickListener(event -> dialog.open(onNewRecord.get(), afterSave));

        grid.addComponentColumn(record -> {
            Button delete = new Button(grid.getTranslation("Delete"));
            delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
            delete.addClickListener(event -> {
                getBean(TransactionTemplate.class).executeWithoutResult(transactionStatus -> {
                    try {
                        if (insteadOfDelete != null) {
                            insteadOfDelete.accept(record);
                        } else {
                            getBean(DSLContext.class).attach(record);
                        }
                        record.delete();
                    } catch (DataAccessException e) {
                        Notification.show(e.getMessage());
                    }
                });
            });

            HorizontalLayout horizontalLayout = new HorizontalLayout(delete);
            horizontalLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
            return horizontalLayout;
        }).setTextAlign(ColumnTextAlign.END).setHeader(buttonAdd);

        grid.addSelectionListener(event -> event.getFirstSelectedItem().ifPresent(record -> dialog.open(record, afterSave)));
    }

The problem is that the line insteadOfDelete.accept(record); does not compile:

Error:(47, 52) java: incompatible types: org.jooq.UpdatableRecord<capture#1 of ?> cannot be converted to capture#2 of ? extends org.jooq.UpdatableRecord<?>

I don't understand the problem. If I change

Consumer<? extends UpdatableRecord<?>> insteadOfDelete

to

Consumer<UpdatableRecord<?>> insteadOfDelete

it compiles.

The joys of recursive generics... It could be seen as a design flaw of jOOQ to use them in the UpdatableRecord type hierarchy. You should capture the wild card in a generic type variable of your method. While it might work to some extent when using ? extends UpdatableRecord<?> ? extends UpdatableRecord<?> or even just UpdatableRecord<?> , I think with an <R> type variable, you're going to get cleaner code.

This might work (I only changed the parameters and added the <R> type variable, nothing else):

public static <R extends UpdatableRecord<R>> void addActionColumnAndSetSelectionListener(
    Grid<R> grid,
    EditDialog<R> dialog,
    Callback afterSave, 
    Supplier<R> onNewRecord,
    Consumer<R> insteadOfDelete
) {
    Button buttonAdd = new Button(grid.getTranslation("Add"));
    buttonAdd.addClickListener(event -> dialog.open(onNewRecord.get(), afterSave));
    grid.addComponentColumn(record -> {
        Button delete = new Button(grid.getTranslation("Delete"));
        delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
        delete.addClickListener(event -> {
            getBean(TransactionTemplate.class).executeWithoutResult(transactionStatus -> {
                try {
                    if (insteadOfDelete != null) {
                        insteadOfDelete.accept(record);
                    } else {
                        getBean(DSLContext.class).attach(record);
                    }
                    record.delete();
                } catch (DataAccessException e) {
                    Notification.show(e.getMessage());
                }
            });
        });
        HorizontalLayout horizontalLayout = new HorizontalLayout(delete);
        horizontalLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
        return horizontalLayout;
    }).setTextAlign(ColumnTextAlign.END).setHeader(buttonAdd);
    grid.addSelectionListener(event -> event.getFirstSelectedItem().ifPresent(
        record -> dialog.open(record, afterSave)));
}

Also, I've removed covariance of your individual method parameters, as I think you probably don't need it. Otherwise, remember that Supplier is covariant and Consumer is contravariant. This answer here explains it nicely . This explains your observation that suddenly things compiled when you had an invariant consumer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM