19

作成中のプログラムのインターフェイスに問題が発生しました。自分のオブジェクトの型への参照を受信/返すメソッドの1つを持つインターフェースを作成したいと思います。それは次のようなものでした:

public interface I {
    ? getSelf();
}

public class A implements I {
    A getSelf() {
        return this;
    }
}

public class B implements I {
    B getSelf() {
        return this;
    }
}

インターフェイスへの参照を返したくないので、「?」の場合は「I」を使用できませんが、クラスを返します。検索したところ、Javaで「自己参照」する方法がないことがわかったので、その「?」を単に置き換えることはできません。「self」キーワードなどの例では。実際、私は次のような解決策を思いつきました

public interface I<SELF> {
    SELF getSelf();
}

public class A implements I<A> {
    A getSelf() {
        return this;
    }
}

public class B implements I<B> {
    B getSelf() {
        return this;
    }
}

しかし、それは本当に回避策か何かのように思えます。そうする別の方法はありますか?

4

4 に答える 4

13

インターフェイスを拡張するときに、パラメータとして独自のクラスを使用するように強制する方法があります。

interface I<SELF extends I<SELF>> {
    SELF getSelf();
}

class A implements I<A> {
    A getSelf() {
        return this;
    }
}

class B implements I<A> { // illegal: Bound mismatch
    A getSelf() {
        return this;
    }
}

これは、ジェネリッククラスを作成するときにも機能します。唯一の欠点:にキャストthisする必要がありSELFます。

Andrey Makarovが以下のコメントで指摘しているように、これはジェネリッククラスを作成するときに確実に機能しません。

class A<SELF extends A<SELF>> {
    SELF getSelf() {
        return (SELF)this;
    }
}
class C extends A<B> {} // Does not fail.

// C myC = new C();
// B myB = myC.getSelf(); // <-- ClassCastException
于 2013-05-08T20:11:40.143 に答える
7

Javaは共変の戻り型をサポートしているので、それが1つのオプションです。Aとの両方が:Bから派生しているという事実を利用してください。Object

public interface I {
    Object getSelf();  // or I, see below
}
public class A implements I {
    A getSelf() { return this; }
}
public class B implements I {
    B getSelf() { return this; }
}

重要なのは、リターンタイプが異なっていても、A.getSelf()との両方B.getSelf()がの正当なオーバーライドであるということです。I.getSelf()これは、すべてをのようA扱うことができるため、return型は基本関数の型と互換性があるためです。(これは「共分散」と呼ばれます。)Object

実際、ABはから派生することも知られているので、同じ理由でに置き換えるIことができます。ObjectI

共分散は一般的に良いことです。あるタイプのインターフェースオブジェクトを持っている人は、別のインターフェースIを呼び出しgetSelf()て取得できます。彼女が知る必要があるのはそれだけです。一方、Aオブジェクトを持っていることをすでに知っている人は、呼び出すことができgetSelf()、実際に別のAオブジェクトを取り戻すことができます。追加情報を使用して、より具体的な派生型を取得できますが、その情報が不足している場合でも、インターフェイスの基本クラスで規定されているすべてのものを取得できます。

I x = new A();
A y = new A();

I a = x.foo();    // generic
A b = y.foo();    // we have more information, but b also "is-an" I
A c = (A)x.foo(); // "cheating" (we know the actual type)
于 2011-11-17T03:32:51.983 に答える
3

他にそうする方法があるのだろうかと思っていました。

あなたはそれを次のように書くことができます:

public interface I {
   I getSelf();
}

次に、結果を目的のタイプにキャストします。既存のクラスABクラスはそのまま機能します。

(これは、戻り型の共分散の例です。戻り型の共分散のサポートがJava 5で追加されました。このアプローチでは、古いJDKでコンパイルエラーが発生します。)

ジェネリックスを使用する代替バージョン(回避策と呼んでいますが、実際にはそうではありません)を使用すると、明示的な型キャストを回避できます。ただし、生成されたコードには暗黙の型キャストがあり、実行時に...JITコンパイラがそれを最適化できない限りです。

AFAIK、これ以上の選択肢はありません。

于 2011-11-17T03:30:45.547 に答える
1

他の人が言っているように、実装クラスでリターンタイプをオーバーライドできます。

public interface I {
    public I getSelf();
}

public class A implements I {
    @Override
    public A getSelf() {
        return this;
    }
}

ただし、「なぜ」という2つの質問があります。

1:インターフェイスが実装オブジェクトを返すようにするのはなぜですか?それは私にとってインターフェースと継承の一般的な考えに反しているようです。これがどのように使用されるかの例を示していただけますか?

2:とにかく、なぜこの関数が必要なのですか?a.getSelf()== aの場合、単にaを使用しないのはなぜですか?

于 2011-11-17T03:51:02.803 に答える