73

I have an abstract class A that define abstract methods. This means that, for a class to be instanciable, all the abstract method have to be implemented.

I'd like all my subclasses to implement a constructor with 2 ints as parameters.

Declaring a constructor defeats my purpose, as I want the constructor defined in subclasses and I don't know anything about the implementation. Moreover I cannot declare a constructor as being abstract;

Is there a way to do this ?

Example of what I want:

Lets say that I am defining the API of a Matrix class. In my problem, Matrix cannot change their dimensions.

For a Matrix to be created, I need to provide its size.

Hence, I want all my implementors to provide the constructor with the size as a parameter. This constructor is motivated by the problem, not by an implementation concern. The implementation can do whatever it wants with these, provided that all the semantic of the methods are kept.

Let's say I want to provide a basic implementation of the invert() method in my abstract class. This method will create a new matrix with this inverted dimensions. More specifically, as it is defined in the abstract class, it will create a new instance of the same class as this, using a constructor that takes two ints. As it does not know the instance it will use reflection (getDefinedConstructor) and I want a way to waranty that I'll get it and that it will be meaningfull for the implementation.

dodecaplex
  • 1,119
  • 2
  • 8
  • 10
  • An Elegant solution can be found here : http://stackoverflow.com/questions/6028526/java-force-an-extending-class – Kuldeep S Chauhan Jun 26 '14 at 12:59
  • in fact, it prolly can be done with apt proccesor ... [here (in update)](https://stackoverflow.com/questions/1571905/how-can-i-enforce-a-type-constructor-in-a-concrete-class-through-the-interface-i#answer-1571950) is a mentioned to do this to no arg constructor and there is a link ... and in code from link you can change `visitExecutable` to check not `t.getParameterTypes().isEmpty()` but if parameters are given classes (you can prolly pass it to annotation)... it will work at compile time – Selvin Feb 09 '23 at 16:28

7 Answers7

57

You can't force a particular signature of constructor in your subclass - but you can force it to go through a constructor in your abstract class taking two integers. Subclasses could call that constructor from a parameterless constructor, passing in constants, for example. That's the closest you can come though.

Moreover, as you say, you don't know anything about the implementation - so how do you know that it's appropriate for them to have a constructor which requires two integers? What if one of them needs a String as well? Or possibly it makes sense for it to use a constant for one of those integers.

What's the bigger picture here - why do you want to force a particular constructor signature on your subclasses? (As I say, you can't actually do this, but if you explain why you want it, a solution might present itself.)

One option is to have a separate interface for a factory:

interface MyClassFactory
{
    MyClass newInstance(int x, int y);
}

Then each of your concrete subclasses of MyClass would also need a factory which knew how to build an instance given two integers. It's not terribly convenient though - and you'd still need to build instances of the factories themselves. Again, what's the real situation here?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Lets say that I am defining the API of a Matrix class. In my problem, Matrix cannot change their dimensions. For a Matrix to be created, I need to provide its size. Hence, I want all my implementors to provide the constructor with the size as a parameter. This constructor is motivated by the problem, not by an implementation concern. The implementation can do whatever it wants with these, provided that all the semantic of the methods are kept. – dodecaplex Jul 02 '10 at 09:43
  • 1
    @dodecaplex: But what if you want to create a `FixedSizeMatrix` implementation which is *always* 10 x 10? You can't *call* constructors polymorphically anyway, so why are you trying to restrict the implementation? – Jon Skeet Jul 02 '10 at 10:03
  • 1
    Well then the implementation will not conform to my API... If it has good reason to do it, then, it will provide a zero arg constructor and a 2 args constructor that will raise an Exception if args are not 10x10. This means that I will still be able to create an empty Matrix of the same implementation and of the same size (without knowing the effective implementation), but I'll get an exception if I try to use this implementation for non 10x10 Matrixes. – dodecaplex Jul 02 '10 at 10:59
  • I do not want to restrict the implementation, but to properly define the contract it has to fullfil. The only thing is why shouldn't I be able to include a constructor in the contract ? – dodecaplex Jul 02 '10 at 11:08
  • 3
    @dodecaplex, you could easily declare a constructor in the abstract class that took two ints, and then a no-arg constructor for `FixedSizeMatrix` which looks like `public FixedSizeMatrix() { super(10, 10); }`, like Jon said. APIs are less about constructors (how you build an object) than they are about methods (how you use an object). – matt b Jul 02 '10 at 11:46
  • @mattb The problem with that is that the abstract class would then have the implement its constructor, something that OP was trying to avoid. He _could_ however delegate the implementation of that constructor to an abstract method, but that would violate other principles, alas. – klaar Aug 06 '15 at 10:19
  • You *can't* call constructors polymorphically, but you *can* use reflection: `getClass().getConstructor(Integer.TYPE, Integer.TYPE).newInstance(initargs)` would not throw a `NoSuchMethodException`. – Matthieu Feb 15 '18 at 11:36
  • *You can't force a particular signature of constructor in your subclass* yes, you can... Using apt processor – Selvin Jul 10 '23 at 17:35
6

You could try something like below. The constructor will thrown an exception if the implementing class does not have a constructor with the appropriate arguments.

This is silly. Compare OK and Bad. Both classes are the same, except that OK meets your requirement and thus passes the runtime checks. Thus enforcing the requirement promotes counter-productive busy work.

A better solution would be some sort of Factory.

abstract class RequiresConstructor
{
    RequiresConstructor( int x, int y ) throws NoSuchMethodException
    {
    super();
    System.out.println( this.getClass().getName() ) ;
    this.getClass(). getConstructor ( int.class , int.class ) ;
    }

    public static void main( String[] args ) throws NoSuchMethodException
    {
    Good good = new Good ( 0, 0 );
    OK ok = new OK ();
    Bad bad = new Bad ();
    }
}

class Good extends RequiresConstructor
{
    public Good( int x, int y ) throws NoSuchMethodException
    {
    super( x, y ) ;
    }
}

class OK extends RequiresConstructor
{
    public OK( int x, int y ) throws NoSuchMethodException
    {
    super( x, y ) ;
    throw new NoSuchMethodException() ;
    }

    public OK() throws NoSuchMethodException
    {
    super( 0, 0 ) ;
    }
}

class Bad extends RequiresConstructor
{
    public Bad() throws NoSuchMethodException
    {
    super( 0, 0 ) ;
    }
}
Matthieu
  • 2,736
  • 4
  • 57
  • 87
emory
  • 10,725
  • 2
  • 30
  • 58
3

If you need to define in your interface the internal representation that implementing classes will use, then you are just doing it wrong. Please go read about encapsulation and data abstraction.

If your abstract implementation relies on certain implementation details, then they belong to that abstract class. Meaning, the abstract class should define a constructor which allows it to initialize the internal state needed to allow the abstracted methods to work.

Generally, constructors are intended to create an instance of a class by providing some details of the initial state of that object instance. This does not mean that the instance being constructed should copy a reference to each individual argument as is often the case in most software I see. Therefore, even if Java did offer a construct for forcing the implementation of certain Constructor signatures on subclasses, those subclasses could easily discard the arguments.

Tim Bender
  • 20,112
  • 2
  • 49
  • 58
  • 1
    Well, if I'm defining a Point in a n-dimension space, I know for sure that the point cannot exist in a m-dimension space (m != n). Hence, It seems to me that the dimension of the space is an INHERENT attribute of the Point, regardless of the point implementation. As far as I can tell this is encapsulation and data abstraction. If the dimension is NECESSARILY known for each instance to be created it seems natural to REQUIRE all implementations to provide a constructor that takes this argument. – dodecaplex Jul 02 '10 at 09:39
  • @dodecaplex, Ahh, a point, great example. Let us suppose you are talking about a 2-dimensional point. Well, sure, I can use an x and y value to represent it. Or, I can use a radians/degrees value and a distance value to represent it. Just one example. There could be other ways. You only think you know how one might implement. Anyway, by definition, if you are trying to enforce a representation you are not using data abstraction. – Tim Bender Jul 02 '10 at 16:24
3

A little bit late, but...

Just create a default constructor in your class which is always called as super constructor. In this default constructor you can check all defined constructors with reflection on it's own class object (which is then not the abstract super class but the concrete subclass). If the constructor you want to be implemented is missing, throw a runtime exception.

I'm not a great friend of reflection because it has the taste of hacking through the back door, but sometimes it helps...

Have a look at this example:

import java.lang.reflect.Constructor;

public abstract class Gaga {
  public Gaga() {
    boolean found = false;
    try {
      Constructor<?>[] constructors = getClass().getConstructors();
      for (Constructor<?> c : constructors) {
        if (c.getParameterTypes().length==2) {
          Class<?> class0 = c.getParameterTypes()[0];
          Class<?> class1 = c.getParameterTypes()[1];
          if ( (class0.getName().equals("int") || class0.isAssignableFrom(Integer.class))
              &&  (class1.getName().equals("int") || class1.isAssignableFrom(Integer.class)) )
            found = true;
        }
      }
    } catch (SecurityException e)
    {
      found = false;
    }

    if (!found)
      throw new RuntimeException("Each subclass of Gaga has to implement a constructor with two integers as parameter.");

    //...
  }

}

And a test class:

public class Test {
  private class Gaga1 extends Gaga {
    public Gaga1() { this(0, 0); }
    public Gaga1(int x, Integer y) { }
  }

  private class Gaga2 extends Gaga {

  }

  public static void main(String[] args)
  {
    new Gaga1();
    new Gaga1(1, 5);
    new Gaga2();
    System.exit(0);
  }
}

In the main function the objects of Gaga1 will be created, but the creation of Gaga2 will throw a runtime exception.

But you can't be sure that this constructor is called - you can't even ensure that it is doing the things you want.

This test is only useful if you're working with reflection.

Abdullah
  • 31
  • 1
1

Supper class:

public abstract class SupperClass {
  protected Foo foo;

  //primary constructor
  public SupperClass(Foo foo) {
      this.foo = foo;
  }

  private SupperClass(){
    //with this private constructor, the subclass forced to implement primary constructor
  }

}

Subclass:

public class SubClass extends JLLayer {

  public SubClass(Foo foo) {
      super(foo);
  }
}
0

The question from title is "How can I force a Constructor to be defined in all subclass of my abstract class" and I wana answer to this question and not debate about OP's real needs.

And the answer is:

You can go for APT processor

@SupportedAnnotationTypes("pl.selvin.constructorsconstraints.apt.ConstructorConstraint")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ConstructorConstraintProcessor extends AbstractProcessor {
    private static final TypeVisitor<Boolean, ArrayList<String>> constraintArgsVisitor =
            new SimpleTypeVisitor7<Boolean, ArrayList<String>>() {
                public Boolean visitExecutable(ExecutableType t, ArrayList<String> args) {
                    final List<? extends TypeMirror> types = t.getParameterTypes();
                    if (args.size() != types.size()) {
                        return false;
                    }
                    for (int i = 0; i < args.size(); i++) {
                        if (!args.get(i).equals(types.get(i).toString()))
                            return false;
                    }
                    return true;
                }
            };

    @Override
    public Set<String> getSupportedOptions() {
        final HashSet<String> ret = new HashSet<>();
        ret.add("org.gradle.annotation.processing.aggregating");
        return ret;
    }

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        for (final TypeElement type : annotations) {
            processConstructorConstraintClasses(env, type);
        }
        return true;
    }

        private void processConstructorConstraintClasses(final RoundEnvironment env, final TypeElement type) {
        final Element constructorConstraintElement = processingEnv.getElementUtils().getTypeElement(ConstructorConstraint.class.getName());
        final TypeMirror constructorConstraintType = constructorConstraintElement.asType();
        final HashMap<String, ArrayList<String>> constructorConstraints = new HashMap<>();
        final ArrayList<Element> elements = new ArrayList<>();
        for (final Element element : env.getElementsAnnotatedWith(type)) {
            elements.add(element);
            for (AnnotationMirror am : element.getAnnotationMirrors()) {
                if (am.getAnnotationType().equals(constructorConstraintType)) {
                    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
                        if ("arguments".equals(entry.getKey().getSimpleName().toString()) && entry.getValue() instanceof Attribute.Array) {
                            final Attribute.Array array = (Attribute.Array) entry.getValue();
                            for (final Attribute a : array.values) {
                                final String className = element.toString();
                                final ArrayList<String> arguments;
                                if(constructorConstraints.containsKey(className)) {
                                    arguments = constructorConstraints.get(className);
                                } else {
                                    arguments = new ArrayList<>();
                                    constructorConstraints.put(className, arguments);
                                }
                                arguments.add(a.getValue().toString());
                            }
                        }
                    }
                    break;
                }
            }
        }
        for (Element element : elements) {
            final TypeMirror derived = element.asType();
            for (String className : constructorConstraints.keySet()) {
                final TypeMirror baseType = processingEnv.getElementUtils().getTypeElement(className).asType();
                if(derived.equals(baseType)) {
                    continue;
                }
                if(processingEnv.getTypeUtils().isAssignable(derived, baseType)) {
                    processClass(element, constructorConstraints.get(className));
                }
            }
        }
    }

    private void processClass(Element element, ArrayList<String> arguments) {
        if (!doesClassContainConstructorWithConstraint(element, arguments)) {
            final String needs;
            if (arguments == null || arguments.size() == 0) {
                needs = "a No-Args Constructor";
            } else {
                needs = "a Contrcutor with arguments(" + String.join(", ", arguments) + ")";
            }
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Class " + element + " needs " + needs);
        }
    }

    private boolean doesClassContainConstructorWithConstraint(Element element, ArrayList<String> arguments) {
        for (final Element subElement : element.getEnclosedElements()) {
            if (subElement.getKind() == ElementKind.CONSTRUCTOR && subElement.getModifiers().contains(Modifier.PUBLIC)) {
                final TypeMirror mirror = subElement.asType();
                if (mirror.accept(constraintArgsVisitor, arguments))
                    return true;
            }
        }
        return false;
    }
}

Pros:

  • It will throw compile time error if derived class would not have such constructor

Cons:

  • generic classes like

    class Whatever<T>{
        Whatever(T arg) {}
    }
    
  • multiple constructors

  • it is not incremental

Github page: https://github.com/SelvinPL/ConstructorsConstraints

-fixed multiple base classes annotated casuing strange behaviour

FX I wana use something like

public class QueryProviders {
    private final ArrayList<AbstractQueryProvider> providers = new ArrayList<>();

    @SafeVarargs
    public QueryProviders(SomeClass arg1, ...OtherArgs... ,
                          Class<? extends AbstractQueryProvider>... providerClasses) {
        try {
            for (Class<? extends AbstractQueryProvider> providerClass : providerClasses) {
                final Constructor<? extends AbstractQueryProvider> ctor = providerClass.getConstructor(SomeClass arg1, ...OtherArgs...);
                providers.add(ctor.newInstance(arg1, ...OtherArgs...));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

and class

public abstract class AbstractQueryProvider {
    public AbstractQueryProvider(SomeClass arg1, ...OtherArgs...) {

    }
}

and usage

QueryProviders providers = new QueryProviders(SomeClass arg1, ...OtherArgs...,
            Backup.class,
            GoodsPrices.class, ...OtherDerived...);

where Backup and GoodsPrices are derived from AbstractQueryProvider

And I need compile time error when I forget about such constructor (in other words "force a Constructor")

So with APT processor you will have class like this:

@ConstructorConstraint(SomeClass.class, ...)
public abstract class AbstractQueryProvider {
    public AbstractQueryProvider(SomeClass arg1, ...) {

    }
}

(package: "pl.selvin.constructorsconstraints.apt") with annotation like this:

@Inherited
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface ConstructorConstraint {
    Class<?>[] arguments() default {};
}
Selvin
  • 6,598
  • 3
  • 37
  • 43
-2

Have the abstract class have an abstract method which takes what you would have for parameters. For instance:

public abstract void setSize(int rows,int columns);
yesennes
  • 1,147
  • 1
  • 10
  • 19