Reputation: 11
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
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:
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'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.
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').
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 specialIt'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:
GoldenDelicious non-arg constructor
Because the chain is:
GoldenDelicious(double)
is called.GoldenDelicious(double)
calls the Apple(double)
constructor (because of an explicit super
call).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.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.GoldenDelicious(double)
, and prints GoldenDelicious constructor with weight
.new GoldenDelicious(7)
.At no point is the args-less Apple()
invoked anywhere.
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
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