P.S. Finally back with another quick article on something that I learned during (yet another) late-night discussion.
Motivation
While browsing through the codebase of a brownfield project that I am currently working on as part of my school work, I found the following lines weird at a glance.
public class StorageManager implements Storage {
// other details omitted ...
/**
* Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
*/
public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
super();
this.addressBookStorage = addressBookStorage;
this.userPrefsStorage = userPrefsStorage;
}
The above code belongs to a concrete implementation of an interface named Storage
and the particular line that I was interested in is this: super();
.
Reasons why I felt that the call to super was unusual:
-
StorageManager
does not extend from any parent class, so I supposed calling super means calling theObject
constructor, why would one ever do that? - If somehow the call to super is related to the interface that
StorageManager
is implementing, it makes no sense because interfaces cannot be instantiated anyway. - While I knew that calling super invokes the parent constructor and one could use it to pass relevant parameters up to the parent, I see no practical purpose in the above code.
Discussion
I posted my question in the class forum and was able to get some helpful responses. So the first thing I did to follow up was to try to remove the super();
to verify whether it has a practical purpose. Tests were still passing and no observable issues when I
restart the application. That proved a point: the call to super, in this case, was unnecessary and meaningless.
I was going to conclude with the above fact and also the practical tip of always testing out code to verify my suspicions. However, my professor's followed up comment intrigued me. Particularly, this line below did not make sense to me:
An explicit call to super(...) is needed only if the parent class doesn't have a parameter-less constructor or ...
I was unsure what the above statement was referring to and with the help of a few fellow students who joined the discussion, and some more Googling, I finally understood the statement. This was the epitome of not knowing what I don't know.
Analysis
In order to understand the above statement, we need to know the following facts, which I extracted from the references included at the end of this article.
- If no constructor is defined in a class, Java compiler automatically create a no-argument (no-arg) constructor, that simply issues a super() call. That is:
public class Foo {
}
is equivalent to
public class Foo {
Foo() {
super();
}
}
- The default no-arg constructor will not be automatically generated, if one (or more) constructor was defined. In other words, you need to define the no-arg constructor explicitly if other constructors were defined. That is:
public class Foo {
int bar;
Foo(int bar) {
this.bar = bar;
}
}
does not become the following by default:
public class Foo {
int bar;
Foo(int bar) {
this.bar = bar;
}
Foo() {
super();
}
}
- If the immediate superclass does not have the default constructor (it defines some constructors but does not define a no-arg constructor), you will get a compilation error in doing a super() call. That is:
public class Foo {
int bar;
Foo(int bar) {
this.bar = bar;
}
}
if the above is the Foo
class, then the following will cause compilation error:
public class Bar extends Foo {
Bar() {
super();
}
}
Or this:
public class Bar extends Foo {
Bar() {
// some lines as long as they are not super
}
}
Or this:
public class Bar extends Foo {
// no constructor provided
}
The only valid way is something like this:
public class Bar extends Foo {
Bar() {
super(10);
}
}
- The explanation to the above: Java guarantees that the constructor method of a class is called whenever an instance of that class is created. It also guarantees that the constructor is called whenever an instance of any subclass is created. In order to guarantee this second point, Java must ensure that every constructor method calls its superclass constructor method. Thus, if the first statement in a constructor does not explicitly invoke another constructor with this() or super(), Java implicitly inserts the call super(); that is, it calls the superclass constructor with no arguments. If the superclass does not have a constructor that takes no arguments, this implicit invocation causes a compilation error.
Final example
To summarize, I will do a walkthrough of another example from a StackOverflow post:
public class Base {
public Base(int foo) {
}
}
public class Subclass extends Base {
public Subclass() {
super(15);
}
}
If the constructor of Subclass
does not include the super(15)
call, Java will implicitly insert super()
as the first line of the Subclass
constructor, which will cause a compilation error in the case where the Base
class does not have a parameter-less constructor(Base()
), which unfortunately will not be automatically generated because there is an existing constructor(Base(int foo)
).
Top comments (2)
Correction should be explicit "this implicit invocation causes a compilation error."
Hi,
Thank you for your kind comment!
For this part that you are referring to:
I think I was trying to say that as opposed to something being done explicitly by the author of the code (writing and calling
super()
):super()
and this "implicit" invocation causes a compilation error.I agree that perhaps "implicit" isn't the best way to describe it here but I am glad that you get the point:)