0

我有这个实体关系模型:

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

}

我想创建我的实体('selectedEntity')来读取它的元数据,加载它的引用者并将它们连接到'selectedEntity'。所以我的用法是:

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

}

问题是我得到这个编译错误调用方法'setReferrer':

'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 ?>)'

我知道我加载的 'referrer' 已正确连接到 'relationMetadata' 但我如何才能提示编译器呢?

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

谢谢G。

4

1 回答 1

0

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

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

selectedEntity.setReferrer(relationMetadata, referrer);

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

这是一个更简单的例子:

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

<T>断言两者都具有a相同b的泛型类型参数。我可以这样调用这个方法:

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

我不能这样做,因为没有办法说出来a并且b有相同的类型参数。(除了作为一个人并查看代码。)因此编译器捕获通配符ab彼此不同。

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

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

看到你的评论,我想我有点明白你在做什么,你也许可以在这里使用辅助方法。这在“通配符捕获和辅助方法”中进行了描述。

它的工作方式是:

  • 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<?>或类似的东西,它会使事情复杂化。

捕获助手是执行此类操作的“正确”方式。

于 2014-03-31T09:06:00.723 回答