提供抽象方法的实现但限制可见性

Provide implementation for abstract method but restrict visibility

我在抽象 class 中有一个调用抽象方法的方法,subclasses 必须为其提供实现。

public abstract class AClass {

    public void foo() {
        ...
        fooToImplement();
        ...
    }

    // DON'T CALL THIS METHOD, ONLY PROVIDE IMPLEMENTATION!
    protected abstract void fooToImplement();

}

我想确保子classes 不调用 fooToImplement(),它们应该始终使用 foo()。该行为类似于 "private abstract" 方法,但在 Java.

中是不可能的

有其他选择吗?谢谢!

您可以对此使用 AOP,例如将方面 @Before 添加到 fooToImplement() 并检查调用的堆栈跟踪,如果 fooToImplement() 被调用除 foo() 之外的任何方法,则抛出 IllegalArgumentException,例如:

if(!Thread.currentThread().getStackTrace()[1].getMethodName().equals("foo")) {
   throw new IllegalArgumentException("You musn't call fooToImplement() directly"+ 
", using foo() instead");
}

但是这种方式有两个问题:

  1. 性能
  2. 运行时异常

如果您不希望您的子classes 能够调用此方法,您可以使用策略:将方法的行为提取到接口中并将此接口的实现传递给对象.例如

IStrategy {
  public void fooToImplement();
} 

AClass {
   public AClass(IStrategy impl) {...}

    public void foo() {
      ...
      strategy.fooToImplement();
      ...
    }
}

委托而不是继承。在 java 8 中,这会更容易一些。

如果您的 IStrategy 实现需要访问对象 AClass 的数据,您可以尝试将其实现为内部 class.

如果您希望覆盖该方法,则您的子class 必须可以看到该方法。 您必须使用 class 不扩展 AClass 作为调用者。

public class BClass extends ACLass {
   @Override 
   protected void fooToImplement() {
      System.out.println("override me im famous");
   }
}
public class CClass {
   private BCLass bInstance;
   public void doSomething(){
      bInstance.foo();
      // !!! NO ACCESS TO fooImplement()
   }
}

由于 fooToImplement() 需要对 subclasses 可见才能在那里实现,并且无法区分 "implement visibility" 和 "execution rights",你不能通过继承来做到这一点。

但是,您可以将您的对象与另一个包含 fooToImplement() 的对象组合在一起:

interface FooImplementation {
    void fooToImplement(AClass a);
}

public abstract class AClass {

    private final FooImplementation fooImpl;
    protected AClass(FooImplementation fooImpl) {
         this.fooImpl = fooImpl;
    }

    public void foo() {
        ...
        fooImpl.fooToImplement(this);
        ...
    }

}

但是,这不会阻止 class 以外的任何人使用 yourFooImpl.fooToImplement(yourAClass)。为防止这种情况,您可以创建一个 class 来提供 fooToImplement() 需要的信息,但只能从 AClass:

中实例化
interface FooImplementation {
    void fooToImplement(AClass.AClassFooView a);
}

public abstract class AClass {

    private final FooImplementation fooImpl;

    protected AClass(FooImplementation fooImpl) {
        this.fooImpl = fooImpl;
    }

    public class AClassFooView {
        ...
        private AClassFooView() {
        }
    }

    public void foo() {
        ...
        fooImpl.fooToImplement(this.new AClassFooView());
        ...
    }

}

但是 fooToImplement 可以将对 AClassFooView 的引用传递给其他 classes...

然而,根据您的 class 的实现者,在文档中绝对确定,没有人应该调用 fooToImplement() 也可能是一个替代方案。

最终你必须相信实现者,因为也有可能有人使用反射来访问私有成员,逆向工程+更改+重新编译你的 class 等..