9

I'm writing the constructor of a LoginRequest class which extends a class called JsobObjectRequest (from the Volley framework in Android, but that's completely irrelevant to the question)

With this code :

 public LoginRequest(String username, String password, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) {
        Boolean hasCredentials=(username!=null && password!=null);
        int method=hasCredentials? Method.POST:Request.Method.GET;
        super(method, API_URL_LOGIN, null, responseListener, errorListener);

        this.username=username;
        this.password=password;

    }

I get the error: call to super() must be the first statement in constructor body

Instead, this code compiles just fine:

 public LoginRequest(String username, String password, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) {
        super((username!=null && password!=null)? Method.POST:Request.Method.GET, API_URL_LOGIN, null, responseListener, errorListener);

        this.username=username;
        this.password=password;

    }

But isn't it effectively the exact same thing? In both cases, a couple of trivial computation are made prior to calling the super constructor, based on the values of the parameters passed to the subclass constructor. Why shouldn't the compiler be able to compile the first example given that it can compile the second?

Is the calling-super-constructor-must-be-first-statement specification more simplistic than it would need to be, or am I missing something?

EDIT: this has been wrongly marked as duplicate of Why do this() and super() have to be the first statement in a constructor?. That question is much more generic and asks why super() have to be the first statement at all. The question here is why a case like the one I've posted would defeat those requirements (and it has been satisfactorily answered in this very question)

Community
  • 1
  • 1
matteo
  • 2,934
  • 7
  • 44
  • 59
  • 1
    May it be compiler-dependent ? Another compiler may understand your code and consider it as okay... – kiwixz Oct 02 '14 at 20:28
  • 2
    It is not compiler-dependent. It is stated in JLS. – kraskevich Oct 02 '14 at 20:30
  • 1
    "Is ... more simplistic than it needs to be?" Possibly, but designing the language to make sure things are done safely isn't easy. Swift, which was just released this year, has tried a more complex set of rules, but I don't yet know how much better or worse it is or if it would help in your particular case. – ajb Oct 02 '14 at 20:36
  • 1
    I hope your "too lazy" comment is tongue-in-cheek. The issue isn't whether the *compiler* is too lazy. In order to have a language standard, the rules for what is and isn't allowed before `super()` must be spelled out with a great degree of exactitude (otherwise we might have code that's accepted by one compiler and rejected by another). And this is not easy. It might have been possible to relax the restriction a tiny bit, allowing some assignment statements that don't call any methods, e.g. But doing a "thorough" check is probably not feasible. – ajb Oct 02 '14 at 20:59
  • Yeah it's not the compiler, it's the specification. By "too lazy" I meant that it enforces a condition more restrictive than actually necessary because it's easier to check. That's not to criticize it (it may be difficult to do otherwise), I just wanted to understand if this was the case, or if there was a more concrete reason for that. – matteo Oct 02 '14 at 21:09

1 Answers1

13

Java enforces that the call to super (explicit or not) must be the first statement in the constructor. This is to prevent the subclass part of the object being initialized prior to the superclass part of the object being initialized.

In your case, you don't do anything but local "trivial computation", so all things considered, it would be okay. However, Java's compiler doesn't go to the level of determining whether statements before a call to super actually do any initialization. It just disallows all statements before super().

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 4
    I see, thanks. It's amazing that a compiler, that takes care of such things as detecting unreachable code, isn't capable of checking whether or not code in a constructor may perform actual initializations. – matteo Oct 02 '14 at 21:03
  • 2
    It would be capable of checking things like this. However, perhaps it might take too much time during compilation (guess). – rgettman Oct 02 '14 at 21:08
  • What do you mean subclass part of the object – Rico Oct 02 '14 at 21:18
  • @Rico When you have an instance of a subclass, the object is comprised of fields and methods that belong to both the subclass and all of it's superclasses. rgettman is referring to the parts of the object which belong to the subclass only, and none of it's ancestors. – deyur Oct 02 '14 at 21:22
  • @Rico Yes, any members you have declared in the subclass, especially instance variables, but not those in the superclass(es). – rgettman Oct 02 '14 at 21:24
  • So if super is not the first line, then you can override a super class method and it gets overwritten when super is called? o.O – Rico Oct 02 '14 at 21:26
  • Oh wait. I found what it means elsewhere. To avoid references to entities in super class as it is not yet created O.o – Rico Oct 02 '14 at 21:34