java中的通用方法。如何匹配通用参数

时间:2014-03-31 08:35:19

标签: java generics casting type-inference entity-relationship-model

我有这种实体关系模型:

// Entity interface
public interface Entity<Reference extends Entity<Reference>> extends Iterable<Attribute<Reference, ?>> {

    // set a referrer and the RelationMetadata to this Entity
    <Referrer extends Entity<Referrer>> void setReferrer(RelationMetadata<Referrer, Reference> relationMetadata, Referrer referrer);

    some other methods...

}

// Relation Metadata is a relation descriptor
public class RelationMetadata<Referrer extends Entity<Referrer>, Reference extends Entity<Reference>> {

    some methods...

}

我想创建我的实体(&#39; selectedEntity&#39;)阅读其元数据,加载其引荐来源并将这些引用连接到“selectEntity&#39;”。所以我的用法是:

// obtain the entity metadata
EntityMetadata<E> entityMetadata = EntityManager.getEntityMetadata(selectedEntity.getClass());

// cycle on each relation my entity is reference
for (Iterator<RelationMetadata<? extends Entity<?>, E>> iterator = entityMetadata.getAsReferencesRelationsMetadataIterator(); iterator.hasNext();) {

    // for each relation
    RelationMetadata<? extends Entity<?>, E> relationMetadata = (RelationMetadata<? extends Entity<?>, E>) iterator.next();

    // instance dao     
    Dao<?> referrerDao = DaoManager.getDao(relationMetadata.getReferrer());

    some code..

    // select referrer entity
    Entity<?> referrer = referrerDao.selectByKey(...);

    // set referrer to my 'selectedEntity'
    selectedEntity.setReferrer(relationMetadata, referrer);

}

问题是我获得了这个编译错误调用方法&#39; setReferrer&#39;:

'The method setReferrer(RelationMetadata<Referrer,E>, Referrer) in the type Entity<E> is not applicable for the arguments (RelationMetadata<capture#16-of ? extends Entity<?>,E>, Entity<capture#18-of ?>)'

我知道&#39;推荐人&#39;我已加载正确连接到&#39; relationMetadata&#39;但我怎么能提示编译器?

这是一个非常烦人的问题,我不知道如何解决它。

谢谢 -G。

1 个答案:

答案 0 :(得分:0)

问题是通配符。声明setReferrer的方式你将无法使用这些通配符调用它。对于两个通配符,<Referrer>类型参数的捕获方式不同。

RelationMetadata<? extends Entity<?>, E> relationMetadata;
Entity<?> referrer;

selectedEntity.setReferrer(relationMetadata, referrer);

编译器无法确定两个?是同一类型。用两个不同的通配符调用setReferrer是不安全的。

这是一个更简单的例子:

static <T> void merge(List<T> a, List<T> b) {}

<T>断言ab都具有相同的泛型类型参数。我可以这样称呼这个方法:

List<String> a = new ArrayList<String>();
List<String> b = new ArrayList<String>();
merge(a, b);

显然我不能这样称呼它:

List<String> a = new ArrayList<String>();
List<Double> b = new ArrayList<Double>();
merge(a, b);

但我也不能这样称呼它:

List<?> a = new ArrayList<String>();
List<?> b = new ArrayList<String>();
merge(a, b);

我不能这样做,因为没有办法告诉ab具有相同的类型参数。 (除非是作为一个人并查看代码。)因此编译器捕获ab的通配符彼此不同。

你的类型更复杂,但是你得到的错误也来自同一个问题。

我不知道该告诉你什么,除非你有太多的通配符。你需要想出办法去做你正在做的事情,而用它们就少了。您正在使用的实际类型参数需要传递更多,或者此例程需要成为参数化对象的一部分。

看到你的评论我想我会看到你在做什么,你可以在这里使用帮助方法。这在"Wildcard Capture and Helper Methods"

中有所描述

它的工作方式是:

  • 假设迭代器在每次调用RelationMetadata时返回任何类型的next
  • 但执行的过程确实有单一类型(只是未知),
  • 您可以将循环体移动到捕获类型的单独方法中。

你有一个这样的辅助方法:

static <R extends Entity<R>, E extends Entity<E>> process(
    RelationMetadata<R, E> relationMetadata,
    Entity<E> selectedEntity
) {
    // not sure what the Dao should be, I guessed
    Dao<R> referrerDao = DaoManager.getDao(relationMetadata.getReferrer());

    ...

    R referrer = referrerDao.selectByKey(...);

    selectedEntity.setReferrer(relationMetadata, referrer);
}

理论上,做这样的事情可以捕获RelationalMetadata的类型并一般地使用它。循环将被重构为这样的东西:

for(
    Iterator<RelationMetadata<? extends Entity<?>, E>> iterator = (
        entityMetadata.getAsReferencesRelationsMetadataIterator()
    );
    iterator.hasNext();
) {
    process(iterator.next(), selectedEntity);
}

假设selectedEntity已经在这种情况的上下文中进行了一般性的输入。 (我认为这将是一个Entity<E>。)如果它是Entity<?>或类似的东西会使事情变得复杂。

捕获帮助程序是执行此类操作的“正确”方法。