views:

466

answers:

3

I feel like I'm missing something here; can someone point out what I'm misunderstanding?!

I've got two classes, an Abstract and a Concrete, as follows:

public abstract class Abstract
{
    protected static int ORDER = 1;

    public static void main (String[] args)
    {
        Concrete c = new Concrete("Hello");
    }

    public Abstract()
    {
        Class c = this.getClass();
        System.out.println(ORDER++ + ": Class = " 
            + c.getSimpleName() 
            + "; Abstract's no-arg constructor called.");
    }

    public Abstract(String arg)
    {
        this();
        Class c = this.getClass();
        System.out.println(ORDER++ + ": Class = " 
            + c.getSimpleName() 
            + "; Abstract's 1-arg constructor called.");
    }
}

and

public class Concrete extends Abstract
{
   public Concrete()
   {
      super();
      Class c = this.getClass();
      System.out.println(ORDER++ + ": Class = " 
          + c.getSimpleName() 
          + "; Concrete's no-arg constructor called.");
   }

   public Concrete(String arg)
   {
      super(arg);
      Class c = this.getClass();
      System.out.println(ORDER++ + ": Class = " 
          + c.getSimpleName() 
          + "; Concrete's 1-arg constructor called.");
   }
}

When I run this I get the following output:

1) Class = Concrete; Abstract's no-arg constructor called.
2) Class = Concrete; Abstract's 1-arg constructor called.
3) Class = Concrete; Concrete's 1-arg constructor called.

My question is this: why doesn't the call to this() from Abstract's String arg constructor call this no-arg constructor on Concrete? Or, perhaps more pertinently, is there any way to get Abstract's String arg constructor to call the no-arg constructor on Concrete, allowing a "proper" chaining of Constructors?

Thanks

Ant.

+3  A: 

No - constructor chaining always goes either sideways (in the same type) or upwards (to the parent type).

Don't forget that the call has to be resolved at compile-time - and Abstract doesn't know what other classes are going to derive from it, or what constructors they'll have.

You could call a virtual method within the Abstract constructor, and override that method within Concrete... but I would urge you not to do that. In particular, the constructor body for Concrete won't have been executed yet, and neither will the variable initializers - so it wouldn't be able to do anything with Concrete-specific state. There are some very specific situations where that's the correct thing to do, but they're rare and should be handled with caution.

What are you actually trying to do? Usually I find it's better to have many "sideways" chains leading to a single constructor which has the "upward" chain.

Jon Skeet
Thanks Jon. The fact that getClass knows the Object isa Concrete, but this() calls the Abstract no-arg constructor is what seems a little unnatural to me.The chaining I was trying to achieve would seem to me to have allowed a more natural division of responsibility between constructors. I think I'll have to duplicate code in Concrete's constructors, or move it out into a new method, but since I'm not writing all the Concrete implementations of this Abstract class, that's something I'll have to document for the other developers.
Antony Baxter
So you were trying to treat constructors as if they were virtual methods? Yeah, that's not going to work.
Jon Skeet
+1  A: 

That's simply the way it is (as detailed by Jon Skeet).

You could add an init block to Concrete though:

{
  Class c = this.getClass();
  System.out.println(ORDER++ + ": Class = " 
  + c.getSimpleName() 
   + "; Concrete's init block called.");
}

In contrast to the default constructor, the inizializer block is always called:

1: Class = Concrete; Abstract's no-arg constructor called.
2: Class = Concrete; Abstract's 1-arg constructor called.
3: Class = Concrete; Concrete's init block called.
4: Class = Concrete; Concrete's 1-arg constructor called.
sfussenegger
A: 

the best way to handle this is generally to have all constructors for a class end up using one common constructor, i.e.:

public Abstract() {
  this(null);
}
public Abstract(String arg) {
  // do all Abstract init here
}

public Concrete() {
  this(null);
}
public Concrete(String arg) {
  super(arg);
  // do all Concrete init here
}
james