16

Let's say someone gives you a class, Super, with the following constructors:

public class Super
{
    public Super();
    public Super(int arg);
    public Super(String arg);
    public Super(int[] arg);
}

And let's say you want to create a subclass Derived. How do you conditionally call a constructor in Super?

In other words, what is the "proper" way to make something like this work?

public class Derived extends Super
{
    public Derived(int arg)
    {
        if (some_condition_1)
            super();
        else if (some_condition_2)
            super("Hi!");
        else if (some_condition_3)
            super(new int[] { 5 });
        else
            super(arg);
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user541686
  • 205,094
  • 128
  • 528
  • 886
  • what are some examples of 'some_external_condition'? It seems like any such condition would have to be checked *after* `super` is called. – driangle Feb 10 '12 at 23:09
  • @ggreiner: It doesn't have to be external, actually. I slightly changed the example. It could be simply based on the user's argument (which might more properly be an enum instead of an integer, but that's kinda unrelated). – user541686 Feb 10 '12 at 23:11

5 Answers5

13

Use static factories, and four private constructors.

class Foo {
 public static Foo makeFoo(arguments) {
    if (whatever) {
      return new Foo(args1);
    } else if (something else) {
      return new Foo(args2);
    }
    etc...
  }
  private Foo(constructor1) { 
    ...
  }
  ...
}
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
3

super must be the first statement in a constructor, hence the logic in your sample is not valid.

The proper way is to create the same 4 constructors in your extending class. If you need validation logic you can use e.g., the builder pattern. You can also as suggested in the comments by @davidfrancis make all constructs private and supply a static factory method. E.g.,

public static Derived newInstance(int arg) {
      if (some condition) {
         return new Derived(arg);
      }
      // etc
}
Johan Sjöberg
  • 47,929
  • 21
  • 130
  • 148
  • How do you check the conditions then? – user541686 Feb 10 '12 at 23:10
  • 1
    @Merhdad, you leave that up to the class constructing the object. This can be e.g., a `builder` if you want to perform specific checks. – Johan Sjöberg Feb 10 '12 at 23:11
  • OR have a static method on a class that takes a condition, inspects it and then instantiates and returns an instance of the class. "extra" constructors on Derived could be private in this case. This is not necessarily a way to go, just another recipe. – Yuriy Zubarev Feb 10 '12 at 23:12
  • 1
    I think you need to make all your constructors private and write a static factory method with your selection logic in it – davidfrancis Feb 10 '12 at 23:13
  • @Yuriy: Yeah, I can come up with ways for working around it as well, but I'm looking for the "proper" solution, not just any solution. – user541686 Feb 10 '12 at 23:14
  • @davidfrancis: That's also what I was thinking, but was wondering if there's a better way. I guess not then? – user541686 Feb 10 '12 at 23:15
  • There is no one universal "proper". It's only "proper" in a context. We don't know enough about your context to pass a judgment. – Yuriy Zubarev Feb 10 '12 at 23:16
  • @YuriyZubarev: Ok I thought there might be a well-known solution to the problem, but if there isn't then never mind. (I'm not sure what the context is, either, actually -- it was a question someone else asked me, not something I'd run into.) – user541686 Feb 10 '12 at 23:20
  • @Mehrdad No better way really, maybe refactor into another class but it's the same code. Don't look for perfection, you can refactor later as long as it's all covered with tests ;) – davidfrancis Feb 10 '12 at 23:20
3

Yeah, what @Johan Sjöberg said.

Also looks like your example is highly contrived. There's no magical answer which would clear this mess :)

Usually, if you have such a bunch of constructors it would be a good idea to refactor them as four separate classes (a class should be only responsible for one type of thing).

JHollanti
  • 2,343
  • 8
  • 27
  • 39
  • Yeah, I don't know... it was a problem a friend of mine apparently ran into, not something I ran into. Totally agree with the third paragraph as well. +1 – user541686 Feb 10 '12 at 23:21
  • 4
    I have run into this problem in real code: writing backwards compatible code for android: `LinearLayout` has a different constructors depending on the value of `android.os.Build.VERSION.SDK_INT`. – Timmmm Nov 01 '12 at 11:18
1

You can't do that, but you can do this from the code that calls your class:

        if (some_condition_1)
            new Super();
        else if (some_condition_2)
            new Super("Hi!");
        else if (some_condition_3)
            new Super(new int[] { 5 });
        else
            new Super(arg);
Paul Nikonowicz
  • 3,883
  • 21
  • 39
0

Can't be done as such as super must be first statement in a constructor.

The proper alternative is a builder class and to have one constructor in the derived class for each constructor in the super class.

eg.

Derived d = new DerivedBuilder().setArg(1).createInstance();

public class DerivedBuilder {

    private int arg;

    // constructor, getters and setters for all needed parameters

    public Derived createInstance() {
        // use appropriate constructor based on parameters
        // calling additional setters if need be
    }
}
Dunes
  • 37,291
  • 7
  • 81
  • 97