This might seem like an odd thing to want, but is there a way in Java to stop subclasses from adding new methods (including constructors) whilst still allowing subclasses to override methods?
The actual situation is where we have an abstract
class with some abstract methods and a constructor
abstract class A {
abstract A doX();
abstract boolean isY();
public A(String s){ ... };
}
and we want all concrete subclasses of this class to only override these methods and constructor.
This is about enforcing a certain style in our code i.e. stopping other people working on the code from adding things. We could just tell them not to, but that rarely works, so we wondered if there was a programmatic way of achieving this.
Obviously the class cannot be final
. Efficiency isn't paramount - cleaner code is more important.
Update - dynamic approach
As has been pointed out in the answers, there is no way to do this statically as the only way to prevent subclasses being created is using final
, which won't work. But I could use a dynamic approach so my current solution is to add this aspect
to the project (which already uses 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");
}
}
Which will cause their tests to fail if they use any of these new methods.
Using subclasses has several advantages: Reuse of code: Through inheritance, a subclass can reuse methods that already exist in a superclass. Specialization: In a subclass you can add new methods to handle cases that the superclass does not handle. You can also add new data items that the superclass does not need.
The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method that it overrides.
Subclass can not access parent private properties (fields) and methods. It can access public , protected and default properties and methods only.
Subclasses inherit public methods from the superclass that they extend, but they cannot access the private instance variables of the superclass directly and must use the public accessor and mutator methods. And subclasses do not inherit constructors from the superclass.
You cannot do that. Only if classes are final
can you ensure that no subclass can be created.
You can also make methods final (even in abstract classes) so that overriding them is forbidden.
Your best bet is to create an interface, with all methods you want visible, and force all users of your API to access the objects via this interface. This way, even if implementations add their own stuff, said stuff won't be visible.
One solution for this is to implement a factory to return the concrete classes; for "added security", you could put all implementations in the same package as this factory and make constructors package local (but this is often not practical):
public final class MyFactory
{
// ....
public MyInterface getConcrete()
{
return new MyInterfaceImpl();
}
// etc etc -- getStones(), getTar(), getFeathers() and so on
}
Note that builders can also be used for that.
If you really wan't to do this.. one way would be to programatically check in the abstract class constructor that the methods defined in the class are those that are allowed.
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");
}
}
When someone is trying create an instance of 'Disallowed' it would fail. However 'new Allowed().doThis()' will work fine.
A more graceful way to do this would be to introduce a custom annotation + annotation processor and do the same check during the compilation time.
There is no such way. Why would you want to enforce such a coding style?
If you really must enforce such a style you could create a "rule enforcer" which checks your classpath and compares the methods of your abstract parent classes with their sub classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With