DEV Community

loading...
Cover image for Calling super() - [OOP & Java #12]

Calling super() - [OOP & Java #12]

tlylt profile image Liu YongLiang ・4 min read

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;
    }
Enter fullscreen mode Exit fullscreen mode

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:

  1. StorageManager does not extend from any parent class, so I supposed calling super means calling the Object constructor, why would one ever do that?
  2. 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.
  3. 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 {
}
Enter fullscreen mode Exit fullscreen mode

is equivalent to

public class Foo {
  Foo() {
    super();
  }
}
Enter fullscreen mode Exit fullscreen mode

  • 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

does not become the following by default:

public class Foo {
  int bar;
  Foo(int bar) {
    this.bar = bar;
  }
  Foo() {
    super();
  }
}
Enter fullscreen mode Exit fullscreen mode

  • 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

if the above is the Foo class, then the following will cause compilation error:

public class Bar extends Foo {
  Bar() {
    super();
  }
}
Enter fullscreen mode Exit fullscreen mode

Or this:

public class Bar extends Foo {
  Bar() {
  // some lines as long as they are not super
  }
}
Enter fullscreen mode Exit fullscreen mode

Or this:

public class Bar extends Foo {
  // no constructor provided
}
Enter fullscreen mode Exit fullscreen mode

The only valid way is something like this:

public class Bar extends Foo {
    Bar() {
    super(10);
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 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);
   }
}
Enter fullscreen mode Exit fullscreen mode

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)).


References

Discussion (0)

pic
Editor guide