7

階層エンティティのインターフェイスを設計する必要があります。

interface HierarchicalEntity<T extends HierarchicalEntity<T>> {
    T getParent();
    Stream<T> getAncestors();
}

前者がすべての祖先を返すような方法で、デフォルト getAncestors()のメソッドを実装するのは非常に簡単です。getParent()Stream

実装例:

default Stream<T> getAncestors() {
    Stream.Builder<T> parentsBuilder = Stream.builder();
    T parent = getParent();
    while (parent != null) {
        parentsBuilder.add(parent);
        parent = parent.getParent();
    }
    return parentsBuilder.build();
}

しかしthis、ストリームにも含める必要があり、ここで問題が発生します。this次の行は、 タイプHierarchicalEntityが ではなくであるため、正しくありませんT

parentsBuilder.add(this); // type mismatch!

getAncestors()結果に含めるためにインターフェイスを再設計するにはどうすればよいthisですか?

4

3 に答える 3

2

これは、自己参照型を作成するときに繰り返し発生する問題です。this基本型 (またはインターフェイス) では、との割り当て互換性を強制することはできませんT

もちろん、すべてのサブタイプがその制約を満たすと確信している場合は、 thistoの未チェックのキャストを実行できます。ただし、としての参照Tが必要なときはいつでも、この未チェックのキャストを実行する必要があります。thisT

より良い解決策は、次のような抽象メソッドを追加することです

/**
    All subtypes should implement this as:

    public T myself() {
        return this;
    }
 */
public abstract T myself();

次に、自己参照が必要なときはいつでもmyself()代わりにas を使用できます。thisT

default Stream<T> getAncestors() {
    Stream.Builder<T> parentsBuilder = Stream.builder();
    for(T node = myself(); node != null; node = node.getParent()) {
        parentsBuilder.add(parent);
    }
    return parentsBuilder.build();
}

もちろん、サブクラスが as として正しく実装されていることを強制することはできませんmyself()return this;、少なくとも、実行時に実装されているかどうかを簡単に確認できます。

assert this == myself();

この参照比較は非常に安価な操作であり、myself()が常に を返すように正しく実装されている場合this、HotSpot はこの比較が常に行われることを事前に証明しtrue、チェックを完全に省略できます。

欠点は、各特殊化で のこの冗長な実装が必要になることですがmyself() { return this; }、一方で、未チェックの型キャストが完全にないことです。別の方法は、チェックされていない操作を型階層の 1 つの場所に制限するために、基本クラスでの非abstract宣言を行うことです。しかし、実際に型かどうかを確認することはできません…</p> myself()@SuppressWarnings("unchecked") T myself() { return (T)this; }thisT

于 2016-05-09T10:03:11.407 に答える
1

aは必ずしも a ではないため、追加thisは失敗します。未知のサブタイプである可能性があります。ただし、 aは、そのように宣言したため、常に aです。HierarchicalEntity<T>TTHierarchicalEntity<T>

getAncestorsとの戻り値の型をStream.BuilderからTに変更するHierarchicalEntity<T>と、 を追加できるようになりますthis

default Stream<HierarchicalEntity<T>> getAncestors() {
    Stream.Builder<HierarchicalEntity<T>> parentsBuilder = Stream.builder();
    T parent = getParent();
    while (parent != null) {
        parentsBuilder.add(parent);
        parent = parent.getParent();
    }
    parentsBuilder.add(this);
    return parentsBuilder.build();
}

一貫性のためgetParentに a を返すことを宣言することもできます。HierarchicalEntity<T>

于 2016-05-06T18:18:57.657 に答える