Java abstract method with abstract parameter and inheritance

大兔子大兔子 提交于 2020-01-02 08:13:59

问题


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

The API is an abstract class:

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:

% java Main
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 would like to now how I should design my API and implementation so that it is efficient.

Of course, I could provide sum(A a) in class be and check if b is an instanceOf B before calling either sum(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 as my "abstract" implementation.


回答1:


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 :)




回答2:


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.




回答3:


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."



来源:https://stackoverflow.com/questions/3148192/java-abstract-method-with-abstract-parameter-and-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!