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 的 sum 不会覆盖 A 的 sum,因为它的签名不同,但我想为有效类型 B 的对象提供 sum 的有效实现。我认为这样的设计非常经典,我想知道我如何应该设计我的 API 和实现,使其高效。

当然,我可以sum(A a)在 B 类中提供并检查 b 是否是instanceofB,然后再调用其中之一sum(B b)或 super,但我认为instanceof出于效率原因应该避免这种情况。(如果效率低下,我的抽象实现可能效率更低)

4

3 回答 3

4

instanceof通常可以通过使用访问者模式来避免。根据您的需要,它可能会也可能不会过大。它很灵活,但相当冗长。在下面的示例中,我删除abstractA以说明它如何与不同类型一起使用。

诀窍是当一个对象被要求访问一个访问者时,对象本身会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

免责声明;不久前我写了一个访问者,所以如果我在这个(几乎未经测试的)代码片段中有任何错误,请纠正我。或者更好的是,自己编辑帖子并改进它:)

于 2010-06-30T10:30:34.437 回答
3

由于B实例可以与A使用的实例相加myA.sum(myB),因此您应该能够更改B' 的定义sum以使其覆盖,除非当然是占位符并且sum应该是可交换的。

更新:

如果这还不够,您可以开始对泛型产生兴趣。这是我的意思的粗略传球:

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 是正确的,普遍接受的解决方法是使用访问者模式。我将这些作为不太完整但不太冗长的替代方案提供。

于 2010-06-30T10:11:37.113 回答
0

那么,是什么让你觉得instanceof慢?它在 JDK 中的几个地方使用,他们希望为抽象类或接口的某些众所周知的实现提供“快速路径”。通常的建议在这里适用:“测试,不要猜测。”

于 2010-06-30T11:56:54.653 回答