2

I have an interface

public interface HistoryDao<B extends UpdatableRecord<B>, H extends UpdatableRecord<H>> extends TableDao<H>{
    default void saveHistoryForIds(List<Integer> ids) {
        HashMap<? extends TableField<H, ? extends TableField<H, ? extends Serializable>>, ? extends TableField<B, ? extends TableField<B, ? extends Serializable>>> mappings = setUpHistoryMapping();

        List<? extends TableField<H, ? extends Serializable>> tableFields = new ArrayList<>(mappings.keySet());

        ArrayList<H> hs = new ArrayList<>();
        jooq()
            .insertInto(table(), tableFields);

    }

    HashMap<? extends TableField<H, ? extends TableField<H, ? extends Serializable>>, ? extends TableField<B, ? extends TableField<B, ? extends Serializable>>> setUpHistoryMapping();
}

(TableDao is an interface but it's not super important)

And In my concrete class I try to implement

MyClassHere implements HistoryDao<ApprovalWorkflowRecord, ApprovalWorkflowHistoryRecord>

@Override
public HashMap<? extends TableField<ApprovalWorkflowHistoryRecord, ? extends TableField<ApprovalWorkflowHistoryRecord, ? extends Serializable>>, ? extends TableField<ApprovalWorkflowRecord, ? extends TableField<ApprovalWorkflowRecord, ? extends Serializable>>> setUpHistoryMapping() {
    HashMap<? extends TableField<ApprovalWorkflowHistoryRecord, ? extends TableField<ApprovalWorkflowHistoryRecord, ? extends Serializable>>, ? extends TableField<ApprovalWorkflowRecord, ? extends TableField<ApprovalWorkflowRecord, ? extends Serializable>>> x = new HashMap<>();

    TableField<ApprovalWorkflowHistoryRecord, Integer> id = APPROVAL_WORKFLOW_HISTORY.ID;

    x.put(id, APPROVAL_WORKFLOW.ID);
}

I think my issue is something to do with covariance... (maybe) but I keep getting this error

x.put(id, APPROVAL_WORKFLOW.ID);

method Map.put(CAP#1,CAP#2) is not applicable
  (argument mismatch; TableField<ApprovalWorkflowHistoryRecord,Integer> cannot be converted to CAP#1)

method AbstractMap.put(CAP#1,CAP#2) is not applicable
  (argument mismatch; TableField<ApprovalWorkflowHistoryRecord,Integer> cannot be converted to CAP#1)

method HashMap.put(CAP#1,CAP#2) is not applicable
  (argument mismatch; TableField<ApprovalWorkflowHistoryRecord,Integer> cannot be converted to CAP#1)   where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends TableField<ApprovalWorkflowHistoryRecord,? extends TableField<ApprovalWorkflowHistoryRecord,? extends Serializable>> from capture of ? extends TableField<ApprovalWorkflowHistoryRecord,? extends TableField<ApprovalWorkflowHistoryRecord,? extends Serializable>> CAP#2 extends TableField<ApprovalWorkflowRecord,? extends TableField<ApprovalWorkflowRecord,? extends Serializable>> from capture of ? extends TableField<ApprovalWorkflowRecord,? extends TableField<ApprovalWorkflowRecord,? extends Serializable>>
Community
  • 1
  • 1
Luke Xu
  • 2,302
  • 3
  • 19
  • 43

1 Answers1

7

If you ignore all the (admittedly impressive) generic noise, your problem boils down to the classic problem of putting a value in a collection with wildcards:

Map<?, ?> x = new HashMap<>();
x.put("a", "b"); // Doesn't work.

Just remove all the wildcards where you don't need them and replace them by concrete types. As a general rule of thumb:

  • Use wild cards in method arguments to allow method callers to pass some less specific collections.
  • Use wild cards when you put generic types into collections, e.g. List<Class<?>> or in this case List<TableField<H, ?>>.
  • Try to avoid returning wildcards from methods.

Here's how to fix this in your case:

public interface HistoryDao<
    B extends UpdatableRecord<B>, 
    H extends UpdatableRecord<H>
> extends TableDao<H> {
    default void saveHistoryForIds(List<Integer> ids) {
        HashMap<TableField<H, ?>, TableField<B, ?>> mappings = setUpHistoryMapping();
        List<TableField<H, ?>> tableFields = new ArrayList<>(mappings.keySet());
        jooq().insertInto(table(), tableFields);
    }

    HashMap<TableField<H, ?>, TableField<B, ?>> setUpHistoryMapping();
}

class MyClassHere implements HistoryDao<
    ApprovalWorkflowRecord, 
    ApprovalWorkflowHistoryRecord
> {

    @Override
    public HashMap<
        TableField<ApprovalWorkflowHistoryRecord, ?>, 
        TableField<ApprovalWorkflowRecord, ?>
    > setUpHistoryMapping() {
        HashMap<
            TableField<ApprovalWorkflowHistoryRecord, ?>, 
            TableField<ApprovalWorkflowRecord, ?>
        > x = new HashMap<>();

        x.put(APPROVAL_WORKFLOW_HISTORY.ID, APPROVAL_WORKFLOW.ID);
    }
}

What did I change?

  • Local Map types don't use wildcards on their keys / values anymore. Instead of Map<? extends TableField<...>, ? extends TableField<...>>, just write Map<TableField<...>, TableField<...>>
  • The same is true for the setUpHistoryMapping() method's return type.
  • You don't need ? extends Serializable here. That type bound doesn't add any value. Just use ? instead.
  • You nested TableField<?, TableField<...>> by accident. That doesn't make sense in a jOOQ context.

See also:

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • Yes!! Thank you so much. I was definitely struggling a lot with the wild cards and this makes a lot of sense. Thanks for all the additional info too! – Luke Xu Jul 13 '17 at 14:16