4

私は最近、次のタイプのコードが表示されるAPIと実装の問題に遭遇しました。

public abstract class A {
 public A sum(A a) {
  System.out.println("A.sum(A) called");
  return null;
 }
}

実装は単純なクラスです。

public class B extends A {
 public B sum(B b) {
  System.out.println("B.sum(B) called");
  return null;
 }
}

それを使用することになると、私は次のように書きます。

public class Main {
  public static void main(String[] args) {
    B b = new B();
    A basa = new B();
  
    b.sum(b);
    basa.sum(b);
    basa.sum(basa);
  }  
}

その結果:

B.sum(B) called
A.sum(A) called
A.sum(A) called

シグネチャが異なるため、Bの合計がAの合計をオーバーライドしないことは理解していますが、効果的なタイプBのオブジェクトに対して合計の効率的な実装を提供したいと思います。このような設計は非常に古典的だと思います。効率的になるようにAPIと実装を設計する必要があります。

もちろんsum(A a)、クラスBで提供し、bがBであるかどうかを確認してから、またはsuperinstanceofを呼び出すこともできますが、効率上の理由から、これは避けるべきsum(B b)だと思いました。instanceof(非効率的な場合、私の抽象的な実装ではさらに効率が低下する可能性があります)

4

3 に答える 3

4

instanceof通常、ビジターパターンを使用することで回避できます。あなたのニーズに応じて、それはやり過ぎかもしれないし、そうでないかもしれません。柔軟性はありますが、かなり冗長です。以下の例では、さまざまなタイプでどのように機能するかを説明するためにabstractから削除しました。A

秘訣は、オブジェクトが訪問者を訪問するように求められたときに、オブジェクト自体がaccept訪問者の正しい方法を選択することです。「instanceof」チェックは、ポリモーフィズムによって解決されます。(しかし、それがより効率的であるとは思えinstanceofません。)

interface Visitor {
    public A accept(A a);
    public B accept(B b);
}

class A {
    public A sum(A a) {
        System.out.println("A.sum(A) called");
        return null;
    }

    public A visit(Visitor sv) {
        return sv.accept(this);
    }
}

class B extends A {
    public B sum(B b) {
        System.out.println("B.sum(B) called");
        return null;
    }

    public B visit(Visitor sv) {
        return sv.accept(this);
    }
}

public class Test {

    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        A basa = new B();

        a.visit(new SumVisitor(b));        // a.sum(b);
        b.visit(new SumVisitor(b));        // b.sum(b);
        basa.visit(new SumVisitor(b));     // basa.sum(b);
        basa.visit(new SumVisitor(basa));  // basa.sum(basa);
    }

    static class SumVisitor implements Visitor {
        A arg;
        SumVisitor(A arg) { this.arg = arg; }
        public A accept(A a) { return a.sum(arg); }
        public B accept(B b) { return b.sum(arg); }
    }
}

出力:

A.sum(A) called
B.sum(B) called
B.sum(B) called
B.sum(B) called

Disclamer; 少し前に訪問者を書いたので、この(ほとんどテストされていない)コードスニペットにバグがある場合は修正してください。または、投稿を自分で編集して改善することをお勧めします:)

于 2010-06-30T10:30:34.437 に答える
3

インスタンスはを使用Bしてインスタンスと合計できるため、もちろんプレースホルダーであり、可換である必要がない場合を除いて、の定義を変更してオーバーライドできるようにする必要があります。AmyA.sum(myB)Bsumsum

アップデート:

これが不十分な場合は、ジェネリックスに夢中になる可能性があります。これが私が言っていることの大まかなパスです:

public abstract class A {
    public <T extends A> T sum(T a) {
        System.out.println("A.sum(A) called");
        return null;
    }

    public static void main(String args[]) {
        B b = new B();
        b.sum(b);

        A basa = new B();
        basa.sum(b);
        basa.sum(basa);
    }

    public static class B extends A {
        @Override
        public <T extends A> T sum(T b) {
            System.out.println("B.sum(B) called");
            return null;
        }
    }
}

@aioobeは、一般的に受け入れられている回避策は、Visitorパターンを使用することです。これらは、完全ではありませんが、冗長性の低い代替手段として提供しています。

于 2010-06-30T10:11:37.113 に答える
0

instanceofそれで、あなたが遅いと思う理由は何ですか?これは、抽象クラスまたはインターフェースの特定のよく知られた実装に「高速パス」を提供したいJDKのいくつかの場所で使用されます。ここでは通常のアドバイスが適用されます:「テスト、推測しないでください」。

于 2010-06-30T11:56:54.653 に答える