简体   繁体   中英

Java: Abstract class constructors and this()

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?

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.

You should have to know that a child class is always hidden from the parent class. You can't call the method or constructor of child class directly like you did in child class.

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.

这里是其重点放在什么需要抽象类的,以及它如何works.May这将帮助你。

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

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM