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

読み込んだ「リファラー」が「relationMetadata」に正しく接続されていることはわかっていますが、それをコンパイラーに知らせるにはどうすればよいですか?

これは非常に厄介な問題です。解決方法がわかりません。

Gさん、ありがとうございます。

4

1 に答える 1

0

問題はワイルドカードです。方法setReferrerは、これらのワイルドカードを使用して呼び出すことができないと宣言されています。型引数は<Referrer>、2 つのワイルドカードに対して異なる方法でキャプチャされます。

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

selectedEntity.setReferrer(relationMetadata, referrer);

?2 つの s が同じ型であることをコンパイラが判断する方法はありません。setReferrer2 つの異なるワイルドカードで呼び出すのは安全ではありません。

より簡単な例を次に示します。

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

それを伝える方法がなく、同じ型引数aを持つ方法がないため、それはできません。b(人間としてコードを見る場合を除いて。) したがって、コンパイラはaとのワイルドカードをb互いに異なるものとしてキャプチャします。

あなたの型はより複雑ですが、あなたが得ているエラーは同じ問題の結果です。

ワイルドカードが多すぎることを除けば、何を伝えたらよいかわかりません。何をするにしても、それらをあまり使用しない方法を考え出す必要があります。作業している実際の型引数をさらに渡す必要があるか、このルーチンをパラメーター化されたオブジェクトの一部にする必要があります。

あなたのコメントを見て、私はあなたが何をしているのか、ここでヘルパーメソッドを使用できるかもしれないと思います。これについては、「ワイルドカード キャプチャとヘルパー メソッド」で説明されています。

仕組みは次のとおりです。

  • 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 に答える