1

Here's a contrived example: (Naming scheme used in actual code is too confusing)

I've got a class Father that extends Grandfather which is a part of a 3rdparty library that cannot be altered. I also have a few classes that extend Father; Son for example. Grandfather has a constructor that looks like this:

public Grandfather(String name) {...}

What actually happens inside this constructor is not important. What is important is that whatever it is doing MUST also be done by all derived classes, thus a call to super(String) is the obligatory first line of every derived class' constructor(s). The implication here is that the constructors of all descendants of Grandfather, no matter how far removed must always make a call to super (or a chained constructor that calls super) as its first line.

Inside my Father implementation I have a method configure(String xml) which parses an xml file and uses reflection to set various parameters inside the instance. Some of these parameters are in Grandfather, some are in Father and some may even be in Son. Configure needs to run every time a class derived from Father is instantiated. My initial thought was to simply add a call to configure(String) to my Father constructor like this:

public Father(String name, String xml) extends Grandfather {
  super(name);
  configure(xml);
}

This works fine for Granfather and Father instantiations but breaks down on Son instantiations for essentially the same reason that calling an overridable method from within a constructor will typically break down. Consider the following Son implementation:

public class Son extends Father {
  private String occupation = "unknown";

  public Son(String name, String xml) {
    super(name, xml);
  }
}

Here's what happens:

  1. A new Son is instantiated using new Son(String, String).
  2. Within Son(String, String) a call to super(String, String) is made, ie. Father(String, String).
  3. Within Father(String, String) configure(String) is called.
  4. configure(String) finds an <occupation> element and using reflection invokes setOccupation("Coal Miner").
  5. Father(String, String) exits and execution returns to Son(String, String).
  6. Right before Son exits all members with supplied defaults are set (this is inlined at the end of the constructor in Java)
  7. occupation is overwritten by the inlining and is set to "unknown".

I'd like to avoid repetitive calls to configure(String) since it is a potentially expensive operation, so adding a call to configure(String) at the end of every constructor is out. (This would result in configure(String) being called twice for Son instances and n times for n'th generation descendants.

I'd also like to avoid having to manually call configure(String) after instantiating Father or any of it's descendants:

Son s = new Son(...);
s.configure(...);

A factory method would work beautifully here but due to the way that Grandfather instances are used, it is not possible. Long story short, I am almost never the one that instantiates these classes and have zero access to the code that does.

Or in other words, doing the following:

Father f = new Father(...);

would result in configure(...) being called exactly one time by the Father constructor while at the same time:

Son s = new Son(...);

would result in configure(...) being called exactly one time by the Son constructor.

Given these constraints, can anybody think of a way to accomplish my objective or am I screwed?

Nick
  • 8,181
  • 4
  • 38
  • 63
  • Is `steps-7` your problem? If yes, when `step 4` is executed, is it setting occupation in son.occupation or there is another variable in either father or GrandFather? – Yogendra Singh Oct 08 '12 at 03:22
  • @YogendraSingh: It's the one in the Son but later overwritten by its own constructor, I guess. – Bhesh Gurung Oct 08 '12 at 03:23
  • That field exists only within Son so yes to son.occupation. I should have noted that in my question - sorry! – Nick Oct 08 '12 at 03:24
  • In that case, your code snippet for `configure(String)` using reflection might be helpful. Would you mind sharing that? – Yogendra Singh Oct 08 '12 at 03:25
  • Correct - overwritten by the implicit inlining of occupation = "unknown" at the end of Son(String, String). – Nick Oct 08 '12 at 03:25
  • Have you checked in debugger that `@step-6`, `son.occupation` is set to `Coal Miner`? I doubt the reflection code is using the right object instance to set the value. – Yogendra Singh Oct 08 '12 at 03:28
  • Step 7 is the problem only when trying to solve the problem by calling configure(...) from within a Father constructor, which is considered bad OO design anyway. I only outlined it here because it's the obvious solution but wont work. – Nick Oct 08 '12 at 03:28
  • Does the Father need to extend GrandFather? Can't it be a field in Father? Composition instead of Inheritance... – Bhesh Gurung Oct 08 '12 at 03:29
  • Yep, if you set a break point on occupation = "unknown" (a1) and where occupation is set to "Coal Miner" (a2), a2 will be hit first, followed by a1 effectively overwriting the xml configuration. – Nick Oct 08 '12 at 03:30
  • Please share your `configure(String)` method code snippet/fragment, where it uses the reflection to set occupation(don't need full code). I am hopeful of some solution. – Yogendra Singh Oct 08 '12 at 03:34
  • Father MUST extend Grandfather. Moving the field up the hierarchy would destroy the overall OO design. It's not obvious with these particular objects but consider if Father was Vehicle and Son was Airplane and the field in question was wingspan. – Nick Oct 08 '12 at 03:34
  • Isn't Father your own class? Or is it from 3rd party too? – Bhesh Gurung Oct 08 '12 at 03:39
  • Everything from Father down is my own. Grandfather is the only class beyond my control. – Nick Oct 08 '12 at 03:41
  • Can't you remove that congure call from the constructor? That's what's the root of the problem is. Why don't you modify it if it's within your reach? – Bhesh Gurung Oct 08 '12 at 03:43
  • configure(String) is a logically complicated method but I can assure you it works correctly. Continuing with my brief Vehicle/Airplane example, essentially all it does is given a string like this: "Airplane.wing.flap.position=6" getAirplane().getWing.getFlap().setPosition(6) will be invoked successfully regardless of whether it is invoked via the Airplane constructor or the Vehicle constructor. T – Nick Oct 08 '12 at 03:44
  • Here's a better description of why this is a problem: http://stackoverflow.com/questions/3330390/state-of-derived-class-object-when-base-class-constructor-calls-overridden-metho – Nick Oct 08 '12 at 03:44
  • Sure I can remove it. Thats not the problem. The problem is where to put it. – Nick Oct 08 '12 at 03:45
  • Updated my question to more clearly describe my objective. – Nick Oct 08 '12 at 03:54

1 Answers1

1

from you statement it seems that you are not really against explicitly calling configure at the end of ctor.

So, is this twist acceptable?

class GrandFather {
  GrandFather(String name, boolean configure) {
    // some init
    if (configure) {
      configure(name);
    }
  }

  GrandFather(String name) {
    this(name, true);
  }
}

class Father {
  Father(String name, boolean configure) {
    super(name, false);

    // some init
    if (configure) {
      configure(name);
    }
  }

  Father(String name) {
    this(name, true);
  }
}

class Son {
  Son(String name, boolean configure) {
    super(name, false);

    // some init
    if (configure) {
      configure(name);
    }
  }

  Son(String name) {
    this(name, true);
  }
}

The idea is, each inherited class call configure explicitly in their constructor, but it is asking the parent not to configure itself.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • That is very clever! I think it just might work too! Gimme a few minutes to try it out... – Nick Oct 08 '12 at 04:00
  • 1
    +1. Almost exactly what I was about to write but more clever. I was about to suggest something like this in this `if(getClass().equals(Father.class))`, that boolean parameter never came to my mind. That's awesome. – Bhesh Gurung Oct 08 '12 at 04:06
  • Thats an interesting approach as well. Avoids the need for additional constructors. – Nick Oct 08 '12 at 04:20
  • Thanks for the praises :P I do like the idea of `getClass().equals(Father.class)` approach too. – Adrian Shum Oct 08 '12 at 06:06