这可能看起来很奇怪,但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");
}
}
如果他们使用任何这些新方法,将导致他们的测试失败。
答案 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社区崩溃。您无法阻止向其子类添加方法。只有你可以阻止他们扩展你的类并覆盖你的方法。