Why do this() and super() have to be the first statement in a constructor?

前端 未结 21 1923
北荒
北荒 2020-11-21 23:10

Java requires that if you call this() or super() in a constructor, it must be the first statement. Why?

For example:

public class MyClass {
    publi         


        
相关标签:
21条回答
  • 2020-11-21 23:45

    Can you give a code example where, if the compiler did not have this restriction, something bad would happen?

    class Good {
        int essential1;
        int essential2;
    
        Good(int n) {
            if (n > 100)
                throw new IllegalArgumentException("n is too large!");
            essential1 = 1 / n;
            essential2 = n + 2;
        }
    }
    
    class Bad extends Good {
        Bad(int n) {
            try {
                super(n);
            } catch (Exception e) {
                // Exception is ignored
            }
        }
    
        public static void main(String[] args) {
            Bad b = new Bad(0);
    //        b = new Bad(101);
            System.out.println(b.essential1 + b.essential2);
        }
    }
    

    An exception during construction almost always indicates that the object being constructed could not be properly initialized, now is in a bad state, unusable, and must be garbage collected. However, a constructor of a subclass has got the ability to ignore an exception occurred in one of its superclasses and to return a partially initialized object. In the above example, if the argument given to new Bad() is either 0 or greater than 100, then neither essential1 nor essential2 are properly initialized.

    You may say that ignoring exceptions is always a bad idea. OK, here's another example:

    class Bad extends Good {
        Bad(int n) {
            for (int i = 0; i < n; i++)
                super(i);
        }
    }
    

    Funny, isn't it? How many objects are we creating in this example? One? Two? Or maybe nothing...

    Allowing to call super() or this() in the middle of a constructor would open a Pandora's box of heinous constructors.


    On the other hand, I understand a frequent need to include some static part before a call to super() or this(). This might be any code not relying on this reference (which, in fact, already exists at the very beginning of a constructor, but cannot be used orderly until super() or this() returns) and needed to make such call. In addition, like in any method, there's a chance that some local variables created before the call to super() or this() will be needed after it.

    In such cases, you have the following opportunities:

    1. Use the pattern presented at this answer, which allows to circumvent the restriction.
    2. Wait for the Java team to allow pre-super() and pre-this() code. It may be done by imposing a restriction on where super() or this() may occur in a constructor. Actually, even today's compiler is able to distinguish good and bad (or potentially bad) cases with the degree enough to securely allow static code addition at the beginning of a constructor. Indeed, assume that super() and this() return this reference and, in turn, your constructor has

    return this;
    

    at the end. As well as the compiler rejects the code

    public int get() {
        int x;
        for (int i = 0; i < 10; i++)
            x = i;
        return x;
    }
    
    public int get(int y) {
        int x;
        if (y > 0)
            x = y;
        return x;
    }
    
    public int get(boolean b) {
        int x;
        try {
            x = 1;
        } catch (Exception e) {
        }
        return x;
    }
    

    with the error "variable x might not have been initialized", it could do so on this variable, making its checks on it just like on any other local variable. The only difference is this cannot be assigned by any means other than super() or this() call (and, as usual, if there is no such call at a constructor, super() is implicitly inserted by compiler in the beginning) and might not be assigned twice. In case of any doubt (like in the first get(), where x is actually always assigned), the compiler could return an error. That would be better than simply return error on any constructor where there is something except a comment before super() or this().

    0 讨论(0)
  • 2020-11-21 23:45

    It makes sense that constructors complete their execution in order of derivation. Because a superclass has no knowledge of any subclass, any initialization it needs to perform is separate from and possibly prerequisite to any initialization performed by the subclass. Therefore, it must complete its execution first.

    A simple demonstration:

    class A {
        A() {
            System.out.println("Inside A's constructor.");
        }
    }
    
    class B extends A {
        B() {
            System.out.println("Inside B's constructor.");
        }
    }
    
    class C extends B {
        C() {
            System.out.println("Inside C's constructor.");
        }
    }
    
    class CallingCons {
        public static void main(String args[]) {
            C c = new C();
        }
    }
    

    The output from this program is:

    Inside A's constructor
    Inside B's constructor
    Inside C's constructor
    
    0 讨论(0)
  • 2020-11-21 23:46

    Before you can construct child object your parent object has to be created. As you know when you write class like this:

    public MyClass {
            public MyClass(String someArg) {
                    System.out.println(someArg);
            }
    }
    

    it turns to the next (extend and super are just hidden):

    public MyClass extends Object{
            public MyClass(String someArg) {
                    super();
                    System.out.println(someArg);
            }
    }
    

    First we create an Object and then extend this object to MyClass. We can not create MyClass before the Object. The simple rule is that parent's constructor has to be called before child constructor. But we know that classes can have more that one constructor. Java allow us to choose a constructor which will be called (either it will be super() or super(yourArgs...)). So, when you write super(yourArgs...) you redefine constructor which will be called to create a parent object. You can't execute other methods before super() because the object doesn't exist yet (but after super() an object will be created and you will be able to do anything you want).

    So why then we cannot execute this() after any method? As you know this() is the constructor of the current class. Also we can have different number of constructors in our class and call them like this() or this(yourArgs...). As I said every constructor has hidden method super(). When we write our custom super(yourArgs...) we remove super() with super(yourArgs...). Also when we define this() or this(yourArgs...) we also remove our super() in current constructor because if super() were with this() in the same method, it would create more then one parent object. That is why the same rules imposed for this() method. It just retransmits parent object creation to another child constructor and that constructor calls super() constructor for parent creation. So, the code will be like this in fact:

    public MyClass extends Object{
            public MyClass(int a) {
                    super();
                    System.out.println(a);
            }
            public MyClass(int a, int b) {
                    this(a);
                    System.out.println(b);
            }
    }
    

    As others say you can execute code like this:

    this(a+b);
    

    also you can execute code like this:

    public MyClass(int a, SomeObject someObject) {
        this(someObject.add(a+5));
    }
    

    But you can't execute code like this because your method doesn't exists yet:

    public MyClass extends Object{
        public MyClass(int a) {
    
        }
        public MyClass(int a, int b) {
            this(add(a, b));
        }
        public int add(int a, int b){
            return a+b;
        }
    }
    

    Also you are obliged to have super() constructor in your chain of this() methods. You can't have an object creation like this:

    public MyClass{
            public MyClass(int a) {
                    this(a, 5);
            }
            public MyClass(int a, int b) {
                    this(a);
            }
    }
    
    0 讨论(0)
提交回复
热议问题