100

Given that I have a class Base that has a single argument constructor with a TextBox object as it's argument. If I have a class Simple of the following form:

public class Simple extends Base {
  public Simple(){
    TextBox t = new TextBox();
    super(t);
    //wouldn't it be nice if I could do things with t down here?
  }
}

I will get a error telling me that the call to super must be the first call in a constructor. However, oddly enough, I can do this.

public class Simple extends Base {
  public Simple(){
    super(new TextBox());
  }
}

Why is it that this is permited, but the first example is not? I can understand needing to setup the subclass first, and perhaps not allowing object variables to be instantiated before the super-constructor is called. But t is clearly a method (local) variable, so why not allow it?

Is there a way to get around this limitation? Is there a good and safe way to hold variables to things you might construct BEFORE calling super but AFTER you have entered the constructor? Or, more generically, allowing for computation to be done before super is actually called, but within the constructor?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Stephen Cagle
  • 14,124
  • 16
  • 55
  • 86

9 Answers9

97

Yes, there is a workaround for your simple case. You can create a private constructor that takes TextBox as an argument and call that from your public constructor.

public class Simple extends Base {
    private Simple(TextBox t) {
        super(t);
        // continue doing stuff with t here
    }

    public Simple() {
        this(new TextBox());
    }
}

For more complicated stuff, you need to use a factory or a static factory method.

waxwing
  • 18,547
  • 8
  • 66
  • 82
  • 1
    This would appear to be a good answer to the question concerning how you would use the references to objects created after construction but before super. – Stephen Cagle Feb 20 '10 at 21:19
  • 3
    This is a very handy pattern, and pretty much lets you completely circumvent the requirement that `super()` not appear after anything else in the constructor. In extreme cases you can even chain multiple private constructors together, though I can't think of any situation where I've needed to do that, and if I did I'd probably start wondering if there was a better way to accomplish what I was doing. – Laurence Gonsalves Feb 20 '10 at 21:29
  • Yup. Others had already answered _why_ it works like this in Java, I just wanted to point out one particular workaround. I think that for most other _valid_ cases you can come up with, it's also possible to come up with an acceptable workaround. Now, to contradict myself, there is one case where this bit me: I was extending a class whose constructor had a Class parameter. I wanted to call `super(this.getClass())`, but since you are not allowed to reference `this`, that code was illegal, even though it seems perfectly reasonable that I should be able to know the actual class at this point. – waxwing Feb 20 '10 at 21:32
  • @waxwing - it may seem "perfectly reasonable", but it would entail treating the `getClass()` method as a special case in the JLS. Use `.class` instead. – Stephen C Feb 21 '10 at 02:45
  • Using `.class` was not an option in this case, because I was writing an abstract class that people could extend and I needed the instance's actual class. – waxwing Feb 21 '10 at 12:48
  • @waxwing - how did you solve that problem? I trying to solve the exact same issue right now. – Matt Ball Aug 30 '10 at 19:41
  • @Bears will eat you: I didn't solve it. Each subclass had to explicitly call `super(SubClassName.class);` – waxwing Aug 30 '10 at 19:52
  • Just wondering: did the class you were extending wanted to know `Class` of its own subclass? If so, why didn't it call `getClass()` (which is polymorphic) itself, in its own constructor. If no, then maybe it was not intended to be inherited at all. – John McClane Dec 04 '18 at 00:19
38

I had the same problem with computation before super call. Sometimes you want to check some conditions before calling super(). For example, you have a class that uses a lot of resources when created. the sub-class wants some extra data and might want to check them first, before calling the super-constructor. There is a simple way around this problem. might look a bit strange, but it works well:

Use a private static method inside your class that returns the argument of the super-constructor and make your checks inside:

public class Simple extends Base {
  public Simple(){
    super(createTextBox());
  }

  private static TextBox createTextBox() {
    TextBox t = new TextBox();
    t.doSomething();
    // ... or more
    return t;
  }
}
Flow
  • 23,572
  • 15
  • 99
  • 156
Ben
  • 401
  • 4
  • 2
9

It is required by the language in order to ensure that the superclass is reliably constructed first. In particular, "If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass."

In your example, the superclass may rely on the state of t at construction time. You can always ask for a copy later.

There's an extensive discussion here and here.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 2
    Interestingly, it sounds like Java goes out of its way to prevent one of the most important cases where wrapping code around a superclass constructor call would be useful: ensuring that if a constructor throws an exception, things can get cleaned up. For example, if one of an object's constructor overloads reads out data from a passed-in file, a derived-class constructor might want have an overload which accepts a filename, passes the superclass constructor a newly-opened file, and then closes the file afterward. If the superclass constructor throws, how can the file get closed? – supercat May 21 '13 at 20:13
  • @supercat: You have to design for inheritance or preclude it [Bloch, *EffectiveJava*, §17]; see also this [Q&A](http://stackoverflow.com/q/1371369/230513). – trashgod May 21 '13 at 21:42
  • If the construction of a subclass object requires an "acquire resource; construct superclass object; release resource" pattern, how should that be implemented? Pass the inner constructor an object that implements a single-interface method to be used in case of failure? Vaguely workable, perhaps, at the expense of chaining some extra levels of constructor calls. – supercat May 21 '13 at 21:56
  • I've had the luxury to fix the superclass or use composition; ping me if you pose this as a question. – trashgod May 22 '13 at 12:06
2

Update 15.08.2023:

A CSR; Permit additional statements before this/super in constructors has now been approved for Java 22, which is scheduled for release in March 2024.


It most likely will become possible to do this. Currently, the call to the super-constructor must be the first statement in a constructor. There is a JEP draft, named Statements before super() that seeks to change this. This particular JEP draft was opened in January of 2023, however, the discussion has been ongoing for a few years. It's a continuation of this issue from 2018, opened by Brian Goetz, the lead architect of Java, so there's reason to believe that this will go through now that somebody has taken the time to work on it.

A compiler implementation for this change has already been completed. Once the JLS has been updated, one can freely use statements before the call to the super-constructor, except try {} catch {}, as it was decided to take a conservative approach at this.

So this will be possible:

class Foo extends Bar {

    Foo() {
        System.out.println("Before super");
        super();
        System.out.println("After super");
    }
}
1

You can define a static supplier lambda which can contain more complicated logic.

public class MyClass {

    private static Supplier<MyType> myTypeSupplier = () -> {
        return new MyType();
    };

    public MyClass() {
        super(clientConfig, myTypeSupplier.get());
    }
}
loweryjk
  • 164
  • 1
  • 7
0

The reason why the second example is allowed but not the first is most likely to keep the language tidy and not introduce strange rules.

Allowing any code to run before super has been called would be dangerous since you might mess with things that should have been initialized but still haven't been. Basically, I guess you can do quite a lot of things in the call to super itself (e.g. call a static method for calculating some stuff that needs to go to the constructor), but you'll never be able to use anything from the not-yet-completely-constructed object which is a good thing.

CodingInsomnia
  • 1,843
  • 2
  • 15
  • 19
0

That's how Java works :-) There are technical reasons why it was chosen this way. It might indeed be odd that you can not do computations on locals before calling super, but in Java the object must first be allocated and thus it needs to go all the way up to Object so that all fields are correctly initialized before you can modify them.

In your case there is most of the time a getter that allows you to access the parameter you gave to super(). So you would use this:

super( new TextBox() );
final TextBox box = getWidget();
... do your thing...
jkmartindale
  • 523
  • 2
  • 9
  • 22
David Nouls
  • 1,895
  • 12
  • 21
  • 2
    That “all fields are correctly initialized” is not actually guaranteed in Java (unlike, IIUC, C++) since a superclass constructor could call a non-`final` instance method that a subclass overrides. The override will then not see initialized fields from the subclass (even `final` ones with instance initializers!). Some static analysis tools will rightly warn about this situation. – Jesse Glick Jul 19 '17 at 20:51
0

This is my solution that allows to create additional object, modify it without creating extra classes, fields, methods etc.

class TextBox {
    
}

class Base {

    public Base(TextBox textBox) {
        
    }
}

public class Simple extends Base {

    public Simple() {
        super(((Supplier<TextBox>) () -> {
            var textBox = new TextBox();
            //some logic with text box
            return textBox;        
        }).get());
    }
}
Pavel_K
  • 10,748
  • 13
  • 73
  • 186
0

You can also have a utility method in your util class somewhere, and do some preprocessing there.

For Example

public class Test() extends AnotherTest {
 public Test(object a) {
    super(AppUtils.process(a))
 }
}


public class AppUtils {
    public static object process(object a) {
       //your preprocessing here
       return a;     
    } 
}
Ammar
  • 41
  • 2