Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing subclasses from adding methods

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.

like image 968
selig Avatar asked Jun 17 '13 11:06

selig


People also ask

Can a subclass add 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.

Do subclasses inherit override methods?

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.

Can subclass have its own methods?

Subclass can not access parent private properties (fields) and methods. It can access public , protected and default properties and methods only.

Do subclasses inherit public methods?

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.


3 Answers

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.

like image 106
fge Avatar answered Oct 18 '22 02:10

fge


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.

like image 23
Dev Blanked Avatar answered Oct 18 '22 02:10

Dev Blanked


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.

like image 2
Michael Avatar answered Oct 18 '22 02:10

Michael