Java抽象方法具有抽象参数和继承

发布于 2024-09-07 06:11:52 字数 859 浏览 1 评论 0原文

我最近摸索到一个 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 的对象提供 sum 的有效实现。我认为这种设计非常经典,我想知道我应该如何设计我的 API 和实现,以便它高效的。

当然,我可以在 B 类中提供 sum(A a) 并在调用 sum(B b) 之前检查 b 是否是 B 的实例或 super,但我认为出于效率原因应避免使用 instanceof。 (如果效率低下,我的抽象实现的效率可能会更低)

I recently fumbled into a problem with an API and an implementation where the following type of code appeared:

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

The implementation is a simple class:

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

When it comes to using it I write:

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

Which results in:

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

I understand that B's sum does not override A's sum as its signature is different, but I'd like to provide an efficient implementation of sum for objects of effective type B. I think such design is quite classical and I would like to know how I should design my API and implementation so that it is efficient.

Of course I could provide sum(A a) in class B and check if b is an instanceof B before calling either sum(B b) or super, but I thought that instanceof was to be avoided for efficiency reasons. (if it is inefficient, it may be even less efficient with my abstract implementation)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

九公里浅绿 2024-09-14 06:11:52

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

诀窍在于,当一个对象被要求访问访问者时,该对象本身会在访问者中选择正确的 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

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

instanceof can usually be avoided by using the visitor pattern. Depending on your needs, it may or may not be an overkill. It's flexible but quite verbose. In the example below I removed abstract from A to illustrate how it works with different types.

The trick is that when an object is asked to visit a visitor, the object itself chooses the correct accept method in the visitor. The "instanceof"-check is resolved through polymorphism. (I doubt that it's more efficient than an instanceof though.)

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

Output:

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

Disclamer; It was a while ago I wrote a visitor, so please correct me if I have any bugs in this (almost untested) code snippet. Or better, edit the post yourself and improve it :)

糖粟与秋泊 2024-09-14 06:11:52

由于可以使用 myA.sum(myB)B 实例与 A 实例相加,因此您应该能够更改 BB code> 对 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 是正确的,普遍接受的解决方法是使用访问者模式。我提供这些作为不太完整但不太冗长的替代方案。

Since B instances can be summed with A instances using myA.sum(myB), you should be able to change B's definition of sum so that it does override, unless of course sum is a placeholder and isn't something that should be commutative.

UPDATE:

If this is insufficient, you could start getting fancy with generics. Here's a rough pass at what I mean:

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 is right that the generally accepted work-around is to use the Visitor pattern. I'm offering these as less complete but less verbose alternatives.

梦里°也失望 2024-09-14 06:11:52

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

So, what makes you think instanceof is slow? It's used in several places in the JDK where they want to provide a "fast path" for certain well-known implementations of an abstract class or interface. The usual advice applies here: "Test, don't guess."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文