防止子类添加方法

时间:2013-06-17 11:43:29

标签: java inheritance abstract

这可能看起来很奇怪,但Java中是否有办法阻止子类添加新方法(包括构造函数),同时仍然允许子类重写方法?

实际情况是我们有一个abstract类,其中包含一些抽象方法和一个构造函数

abstract class A {
  abstract A doX();
  abstract boolean isY();
  public A(String s){ ... };
}

我们希望这个类的所有具体子类只覆盖这些方法和构造函数。

这是关于在我们的代码中强制执行某种样式,即阻止其他处理代码的人添加内容。我们可以告诉他们不要,但这很少有效,所以我们想知道是否有一种实现这一目标的程序化方法。

显然,班级不能是final。效率并不是最重要的 - 更清晰的代码更重要。

更新 - 动态方法

正如答案中指出的那样,没有办法静态地执行此操作,因为防止创建子类的唯一方法是使用final,这将无法正常工作。但我可以使用动态方法,因此我当前的解决方案是将此aspect添加到项目(已使用AspectJ)。

   public aspect WatchA{
      before() : execute(* A.*()) || execute(* A.*(..)) {
         String methodCalled = joinPoint.getSignature().getName();
         Class<?> c = Class.forName(args[0])
         Method[] allMethods = c.getDeclaredMethods();
         boolean found = false;
         for(Method m : allMethods)
           found |= m.getName().equals(methodCalled);
         if(!found) 
           throw new RuntimeException("Do not add method "+methodCalled+" to A");
      }
   }

如果他们使用任何这些新方法,将导致他们的测试失败。

4 个答案:

答案 0 :(得分:8)

你做不到。只有当类为final时才能确保不能创建子类。

你也可以使方法最终(即使在抽象类中),以便禁止覆盖它们。

最好的办法是创建一个界面,其中包含您想要的所有方法,并强制API的所有用户通过此界面访问对象。这样,即使实现添加了自己的东西,也不会显示所说的东西。

对此的一个解决方案是实现工厂以返回具体类;对于“增加安全性”,您可以将所有实现放在与此工厂相同的包中,并使构造函数包本地化(但这通常不实用):

public final class MyFactory
{
    // ....
    public MyInterface getConcrete()
    {
        return new MyInterfaceImpl();
    }
    // etc etc -- getStones(), getTar(), getFeathers() and so on
}

请注意,构建器也可用于此。

答案 1 :(得分:2)

没有这样的方法。 你为什么要强制执行这样的编码风格?

如果你真的必须强制执行这样的风格,你可以创建一个“规则强制执行器”来检查你的类路径,并将你的抽象父类的方法与它们的子类进行比较。

答案 2 :(得分:2)

如果你真的不想这样做..一种方法是以编程方式检查抽象类构造函数,即类中定义的方法是允许的方法。

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public abstract class Base {

    private static final Set<String> allowedMethodNames = new HashSet<>(Arrays.asList("doThis", "wait", "wait", "wait", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll"));

    public Base() {
        Set<String> allMethods = new HashSet<>();
        for (Method aMethod : getClass().getMethods()) {
            allMethods.add(aMethod.getName());
        }
        if (!allowedMethodNames.equals(allMethods)) {
            allMethods.removeAll(allowedMethodNames);
            throw new IllegalStateException("Following methods not allowed <" + allMethods + ">");
        }
    }

    public abstract void doThis();
}

public class Disallowed extends Base {

    @Override
    public void doThis() {
        System.out.println("dooooooo");
    }

    public void doSomethingElse() {
        System.out.println("not allowed");
    }

    public static void main(String[] args) {
            new Allowed().doThis();
        new Disallowed();
    }

}

public class Allowed extends Base {

    @Override
    public void doThis() {
        System.out.println("doing this");
    }


}

当有人尝试创建“禁止”的实例时,它会失败。但是'new Allowed()。doThis()'可以正常工作。

更优雅的方法是引入自定义注释+注释处理器,并在编译期间进行相同的检查。

答案 3 :(得分:1)

Java意味着灵活性。因此,java使您更方便地使用抽象方法并从子类中重写它们。还应该有一个想法,即为这些子类添加新方法。即使java也无法改变这一点。如果确实如此,则整个Java社区崩溃。您无法阻止向其子类添加方法。只有你可以阻止他们扩展你的类并覆盖你的方法。