5

这似乎是一件奇怪的事情,但是在 Java 中有没有办法阻止子类添加新方法(包括构造函数),同时仍然允许子类覆盖方法?

实际情况是我们有一个abstract带有一些抽象方法和构造函数的类

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

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

这是关于在我们的代码中强制执行某种风格,即阻止其他人在代码上添加东西。我们可以告诉他们不要这样做,但这很少奏效,所以我们想知道是否有一种程序化的方式来实现这一点。

显然类不能final。效率不是最重要的——更简洁的代码更重要。

更新 - 动态方法

正如答案中所指出的那样,没有办法静态地做到这一点,因为防止创建子类的唯一方法是 using 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

4 回答 4

8

你不能这样做。只有类是final才能确保不能创建子类。

您还可以使方法成为最终方法(即使在抽象类中),从而禁止覆盖它们。

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

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

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

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

于 2013-06-17T11:45:07.017 回答
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()' 可以正常工作。

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

于 2013-06-17T12:05:49.917 回答
2

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

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

于 2013-06-17T11:44:51.807 回答
1

Java 意味着灵活性。因此,java 让您在使用抽象方法和从子类中覆盖它们时更加方便。还应该有一个向这些子类添加新方法的想法。即使是java也无法改变这一点。如果是这样,那么整个 Java 社区就会崩溃。您不可能阻止将方法添加到它们的子类中。只有您可以阻止他们扩展您的类并覆盖您的方法。

于 2013-06-17T12:32:52.167 回答