Linh Tran
Linh Tran

Reputation: 11

Why does this statement after super keyword not get invoked?

Why does the line

System.out.println("GoldenDelicious non-arg constructor");

not get invoked in this code?

public class Test {
  public static void main(String[] args) {
    Apple a = new Apple();
    System.out.println(a);
    System.out.println("---------------");
    
    GoldenDelicious g = new GoldenDelicious(7);
    System.out.println(g);
    System.out.println("---------------");

    Apple c = new GoldenDelicious(8);
    System.out.println(c);
  }
}

class Apple {
  double weight;
  
  public Apple() {
    this(1);
    System.out.println("Apple no-arg constructor");
  }
  
  public Apple(double weight) {
    this.weight = weight;
    System.out.println("Apple constructor with weight");
  }
  
  @Override 
  public String toString() {
    return "Apple: " + weight;
  }
}
class GoldenDelicious extends Apple {
  public GoldenDelicious() {
    this(5);
    System.out.println("GoldenDelicious non-arg constructor");
  }
  
  public GoldenDelicious(double weight) {
    super(weight);
    this.weight = weight;
    System.out.println("GoldenDelicious constructor with weight");
  }
  
  @Override 
  public String toString() {
    return "GoldenDelicious: " + weight;
  }
}

The output of the code is

Apple constructor with weight Apple no-arg constructor Apple: 1.0
--------------- Apple constructor with weight GoldenDelicious constructor with weight GoldenDelicious: 7.0
--------------- Apple constructor with weight GoldenDelicious constructor with weight GoldenDelicious: 8.0

And why does the line

System.out.println("does this print?");

gets invoked even though I used super(color, filled); to call the superclass's constructor?

public class Circle extends GeometricObject {
    private double radius;

    public Circle() {
    }

    public Circle(double radius) {
        this.radius = radius;
    }

    public Circle(double radius,
                  String color, boolean filled) {
        super(color, filled);
        this.radius = radius;
        System.out.println("does this print?");
    }

    /**
     * Return radius
     */
    public double getRadius() {
        return radius;
    }

    /**
     * Set a new radius
     */
    public void setRadius(double radius) {
        this.radius = radius;
    }

    /**
     * Return area
     */
    public double getArea() {
        return radius * radius * Math.PI;
    }

    /**
     * Return diameter
     */
    public double getDiameter() {
        return 2 * radius;
    }

    /**
     * Return perimeter
     */
    public double getPerimeter() {
        return 2 * radius * Math.PI;
    }

    /* Print the circle info */
    public void printCircle() {
        System.out.println("The circle is created " + getDateCreated() +
                " and the radius is " + radius);
    }

    public static void main(String[] args) {
        System.out.println(new Circle(3.0, "red", true));
    }
}

Upvotes: 0

Views: 112

Answers (2)

rzwitserloot
rzwitserloot

Reputation: 103273

You seem to misunderstand how constructors work.

Constructors are like methods in pretty much every way. You seem to think that ones you call super() or this(), that that's the last thing your constructor can possibly do. Not so - that means the indicated constructor runs in its entirety, and then control returns to your constructor, just like if you call any method: Then that method runs, and once it is then, your code continues.

However, there are a few additional rules:

You MUST refer to another constructor

All constructors MUST 1 start with either a this() call (invoke another one of your constructors), or with a super() call of some stripe (invoking one of your parent's constructors). You must write one of those (but see the 'implicits' section below).

You can only do it precisely once

You can't write class Test { public Test() { super(); this(); }} - you must have precisely one call to this() or super() in each constructor. Not zero. Not two.

You MUST have a constructor

All classes must have one, even abstract ones. You may have more than one. You do not need to have a no-args constructor. (But see the 'implicits').

Implicits

Javac will help you out and add some syntax sugar. Note the term 'syntax sugar': What javac does is identical to what would happen if you wrote it out by hand. The only two things javac does for you if you 'forget', are:

  • If you fail to start a constructor with either a this(zeroOrMoreArgs) or a super(zeroOrMoreArgs), then javac will assume you meant to start it all off with super() and injects it for you. If that would be invalid (for example, because your parent class has no no-args constructor, or it does but it is not accessible, e.g. because it is private, then you get a compiler error exactly the same as if you had explicitly written super(); - absolutely no attempt to intelligently pick a parent is undertaken by javac, and javac will never inject a this().

  • If you fail to write any constructors in a class, then javac will inject public YourClassName() {super();} - in other words, a no-args public constructor that does nothing, which then triggers the first implicit rule, of injecting super(). Just like the previous point, if that would be invalid because the parent class does not have an (accessible) no-args constructor, you get a compiler error identical to the one you get when you write this no-args do-nothing-but-call-super() constructor by hand.

java.lang.Object is special

It's special, in the sense that it gets to break one rule: It has one public no-args constructor that does nothing and doesn't even call super() (because it has no super class). javac cannot compile j.l.Object, the JDK build does it in a special way (Object also doesn't extend anything; ordinarily if you don't have an extends clause, javac assumes you meant to write extends j.l.Object and compiles as if you did write that. You can't tell it not to do that).

With that, all is explained:

Why does it not call my GoldenDelicious non-arg constructor

Because the chain is:

  • GoldenDelicious(double) is called.
  • As first thing, GoldenDelicious(double) calls the Apple(double) constructor (because of an explicit super call).
  • As first thing, Apple(double) calls super() (not this() - as you can tell, by the fact that "Apple no args" isn't shown when you call new GoldenDelicious(7). super() in this context is java.lang.Object's constructor.
  • j.l.Object's constructor runs and does nothing.
  • code control returns back to the Apple(double) constructor. Which prints 'Apple constructor with weight'. Note that we're already on the way back when this prints. Now the Apple(double) constructor has reached its end.
  • Code control returns back to GoldenDelicious(double), and prints GoldenDelicious constructor with weight.
  • The constructor ends, and with that, the new instance is ready and its reference is the value of the expression new GoldenDelicious(7).

At no point is the args-less Apple() invoked anywhere.

Why does 'does it print?' print?

Same reason. Because Circle(double, String, boolean) is called, and it first invokes the super constructor, and then the remainder of that code once the super constructor is done, which includes printing 'does it print?'.


[1] Starting with JDK22 (or was it JDK23?), you no longer need to invoke another constructor as first thing; there is a somewhat restricted set of things you can do before invoking this() or super(). You must, however, still invoke this() or super() exactly once (not twice, not zero times), on a path that is necessarily invoked (and necessarily invoked exactly once; you can't stick it in a loop. Even if your eyeballs and brain tell you that loop must run precisely once, the compiler isn't going to go through that trouble for you).

Upvotes: 0

Tejas R
Tejas R

Reputation: 21

I went through your code, the line you mentioned System.out.println("GoldenDelicious non-arg constructor"); is not getting invoked because you are not creating any object using the no-args constructor of GoldenDelicious class! As that print statement resides in the no-args constructor of GoldenDelicious, go ahead and create an object of GoldenDelicious with no-args and it works.

    public class Test {
    public static void main(String[] args) {
        Apple a = new Apple();
        System.out.println(a);
        System.out.println("---------------");

        GoldenDelicious goldenDelicious = new GoldenDelicious();

        GoldenDelicious g = new GoldenDelicious(7);
        System.out.println(g);
        System.out.println("---------------");

        Apple c = new GoldenDelicious(8);
        System.out.println(c);
    }
}

Upvotes: 2

Related Questions