Don't require `super()` to be the first statement in a constructor


#1

In Java, a constructor must call the superclass constructor before doing anything else.  The following, for example, will not compile:

class Parent {   public Parent(Foo foo, Bar bar) {} }

class Child extends Parent {
  public Child() {
  Foo foo = new foo();
  foo.mutate();

    Bar bar = new Bar();
    bar.mutate();

    super(foo, bar); // ERROR: "Call to 'super()' must be first statement in constructor body"

  }
}


This limitation gets in the way at times.  It is possible to work around it by using a static factory method, but this breaks encapsulation: users of our class have to recompile every time we switch between a constructor and a factory method.  Even though our decision is an implementation detail, it becomes part of our interface, which is unfortunate.

It would be nice if Kotlin threw away this limitation.


#2

This is not a wise idea, it just leaves an uninitialised base class which can result in all sorts of errors. There aren't much use cases either that can't be implemented in a better and safer way.


#3

Well, we can't do much about being unable to write code before the super call, because it's naturally connected to maintaining class invariants (and JVM doesn't like it).

I’d suggest that you always use factory methods if you have such concerns, and we’ll look into how Kotlin could help you.

I can see an analogy with properties: Kotlin doesn’t allow direct field access for the sake of binary compatibility, we generate calls to getters/setters instead. Maybe we could do the same with constructors: always go through a factory method, so that client won’t notice whether you have a constructor or not. There are very many details to it, but the principal idea looks neat.


#4

mplatvoet wrote:

This is not a wise idea, it just leaves an uninitialised base class which can result in all sorts of errors.

Perhaps I’m missing something, but I don’t see how the proposed change would cause problems relating to uninitialised state.  For example, Kotlin won’t compile the following:

open class Parent(value: String) {   fun getValue(): String {   return "test"   } }

class Child(value: String) : Parent(getValue()) // ERROR: "Unresolved reference: getValue"


So it seems to me that Kotlin already has safeguards here.  What difference would it make if there were statements before the superclass-constructor call?


#5

That's an interesting idea.  Thanks for the consideration!


#6

There may not be a conceptual need for the restriction to be this hard. In Swift, for instance, object initialization is split into two parts: first, initialize all members; then call a super constructor; then do arbitrary things. So it’s possible to get more flexibility in a safe way; whether it’s possible in Kotlin and on the JVM I don’t have the knowledge to say.

Note that this would not make tomjb’s example compile (I think). While we could ask the compiler to try and figure out if pre-super() code (potentially) accesses (potentially) uninitialized members, this problem is clearly not computable, so we could only expect a heuristic.