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

时间:2015-12-18 10:42:50

标签: java abstract-class visibility

我在抽象类中有一个方法,它调用一个抽象方法,子类必须为其提供实现。

public abstract class AClass {

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

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

}

我想确保子类不调用fooToImplement(),它们应该总是使用foo()。这种行为类似于“私有抽象”方法,但这在Java中是不可能的。

还有其他选择吗?谢谢!

4 个答案:

答案 0 :(得分:4)

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

IStrategy {
  public void fooToImplement();
} 

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

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

代表团而不是继承。在java 8中,这会更容易一些。

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

答案 1 :(得分:2)

如果希望覆盖该方法,则子类必须可以看到该方法。 你必须使用一个不扩展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()
   }
}

答案 2 :(得分:1)

由于要在那里实现的子类需要fooToImplement(),因此无法区分"实现可见性"和"执行权",你不能通过继承来做到这一点。

然而,您可以将您的对象与另一个包含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);
        ...
    }

}

但是,这不会阻止课外的任何人使用yourFooImpl.fooToImplement(yourAClass)。为了防止这种情况,您可以创建一个类,该类提供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的引用传递给其他类......

但是,根据您的类的实现者在文档中绝对确定,没有人应该调用fooToImplement()也可以作为替代。

最终你必须相信实现者,因为还有可能有人使用反射来访问私有成员,反向改造+更改+重新编译你的类等。

答案 3 :(得分:0)

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

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

然而这种方式有两个问题:

  1. 性能比较
  2. 运行时异常