Java 泛型和 jOOQ - 我如何创建一个 jOOQ 列到另一个列的 HashMap

Java Generics & jOOQ - How can I create a HashMap of one jOOQ column to another

我有接口

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是一个接口,但不是特别重要)

并且在我的具体 class 中,我尝试实现

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);
}

我认为我的问题与协方差有关...(也许)但我一直收到此错误

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

如果您忽略所有(公认的令人印象深刻的)通用噪音,您的问题归结为将值放入带有通配符的集合中的经典问题:

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

只需删除所有不需要的通配符,并用具体类型替换它们。作为一般经验法则:

  • 在方法参数中使用通配符以允许方法调用者传递一些不太具体的集合。
  • 将泛型类型放入集合时使用通配符,例如List<Class<?>> 或在本例中为 List<TableField<H, ?>>.
  • 尽量避免 return 从方法中使用通配符。

根据您的情况解决此问题的方法如下:

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);
    }
}

我改变了什么?

  • 本地 Map 类型不再在它们的键/值上使用通配符。而不是Map<? extends TableField<...>, ? extends TableField<...>>,只写Map<TableField<...>, TableField<...>>
  • setUpHistoryMapping()方法的return类型也是如此。
  • 这里不需要? extends Serializable。该类型绑定不会增加任何值。只需使用 ? 即可。
  • 您不小心嵌套了 TableField<?, TableField<...>>。这在 jOOQ 上下文中没有意义。

另请参阅:

  • Problem using generic map with wildcard
  • put a value into hashmap which uses wildcard in java can not assign value
  • Using Java Generics with HashMap
  • Java Map compiler error with generic
  • What is wrong with my method generic parameters