Why this () and super () constructor must be the first statement?

Java requirements, if you call this () or super () in the constructor, it must be the first statement. why?

E.g:

public class MyClass {
    public MyClass(int x) {}
}

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
    }
}

Sun compiler says "call to the super constructor must be the first statement." Eclipse compiler says "constructor calls the constructor must be the first statement."

However, you can resolve this problem by rearranging some code:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        super(a + b);  // OK
    }
}

This is another example:

public class MyClass {
    public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
    public MySubClassA(Object item) {
        // Create a list that contains the item, and pass the list to super
        List list = new ArrayList();
        list.add(item);
        super(list);  // COMPILE ERROR
    }
}

public class MySubClassB extends MyClass {
    public MySubClassB(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(Arrays.asList(new Object[] { item }));  // OK
    }
}

Therefore, this does not prevent you before calling the super execution logic . It just prevents you from performing logic can not be included in a single expression.

Calls this()have similar rules. The compiler said, "this must be a call to the constructor of the first statement."

Why does the compiler have these restrictions? Could you give a code example, if the compiler does not have this restriction, then bad things happen?


#1st Floor

The parent constructorneeds to subclass constructorcalled before. This will ensure that if you call any method on the parent class constructor, the parent class is set up correctly.

You want to do is to pass args to the super constructor is perfectly legal, you only need to perform operations in the United configuration of these args, or pass them to the constructor, and then pass them to super:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

If the compiler does not enforce this, you can do the following:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

If the parentclass has a default constructor, then compileryou automatically insert a call to super. Since Java each class inherits from Object, so you must call the object constructor in some way, and it must be executed first. The compiler automatically inserted super () can achieve this. Forced super appear first, enforcement constructor body in the correct order, namely: Object -> parent -> child -> ChildOfChild-> SoOnSoForth


#2nd Floor

I'm pretty sure (the person familiar with the Java specification), which is to prevent you from (a) is allowed to use part of the constructed object, and (b) enforce the parent class constructor with "fresh" constructed "object.

Some examples of "bad" things are:

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}

#3rd floor

You can before calling the child's constructor, using anonymous initialization block to initialize the fields in the child. This example demonstrates:

public class Test {
    public static void main(String[] args) {
        new Child();
    }
}

class Parent {
    public Parent() {
        System.out.println("In parent");
    }
}

class Child extends Parent {

    {
        System.out.println("In initializer");
    }

    public Child() {
        super();
        System.out.println("In child");
    }
}

This will output:

In the parents
in the initializer
in children


#4th floor

Because JLS says so. Can you change the JLS in a compatible manner to allow it? Correct.

However, this would complicate the language specification, which is already complicated enough. Doing so is not a very useful thing, and there are many solutions (with the results of static methods or lambda expression this(fn())calls another constructor - This method is called before another constructor, and therefore super constructor). Therefore, the power to weight ratio is unfavorable changes of.

Please note that only this rule does not prevent the use of super-class field before the completion of construction.

Consider these examples illegal.

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

This example is legal, but it is "wrong."

class MyBase {
    MyBase() {
        fn();
    }
    abstract void fn();
}
class MyDerived extends MyBase {
    void fn() {
       // ???
    }
}

In the above example, if the MyDerived.fnrequired MyDerivedstructural parameters of the function, you need ThreadLocalto hide them. ; (

Incidentally, since the Java 1.4, the synthesis comprises an external field thisis assigned before the internal super class constructor is called. This has led to a special target for the earlier version of the compiler code NullPointerExceptionevent.

Also note that in the presence of unsafe publications, unless preventive measures, otherwise you can revisit constructed by other threads.

Edited March 2018: In the message recording: construction and validation Oracle is to imply that this restriction is deleted (but unlike C #, thiswill be before the constructor chain certainly unallocated (DU)).

Historically, this () or super () must first be in the constructor. This limitation has never been popular, and is considered to be arbitrary. There are many reasons for subtle reasons for this limitation, including the verification of invokespecial. Over the years, we have solved these problems in the virtual machine level, so consider lifting this restriction not only to record, but also for all constructors are possible.


#5th Floor

Simply because it is inherited philosophy. And based on the Java language specification, which is the main way to define a constructor:

ConstructorBody:{ExplicitConstructorInvocation opt BlockStatements opt }

The first statement constructor body may be

  • Explicit call to another of the same class constructor (by using the keyword "this"); or
  • Direct superclass of an explicit call (by using the keyword "super")

If the constructor body not explicitly constructor call starts, and part of the constructor declared not the original class Object, the constructor body implicit in the superclass constructor calls "super ();" at the beginning of the superclass constructor calls for its immediate superclass with no parameters. And so on .. constructors will have the whole chain, has been traced back to the constructor of Object; "descendants of the Java platform in all classes are objects." This thing called " constructors link ."

Why now?
The reason why Java ConstructorBody defined in this way is that they need to maintain a hierarchy of objects. Remember the definition of inheritance; it extends the curriculum. That being said, you can not extend something that does not exist. First you need to create the base (super class), then it can derive (subclass). That's why they call them "parents and children" category; you can not live without parents having children.

, Subclass inherits all the members (fields, methods, nested classes) from its parent class technically. And because the constructor is not a member (they are not objects. They are responsible for creating an object), so they are not inherited by subclasses, but you can call them. And since the implementation of a constructor only when you create an object . So, when you create a subclass of objects, how to ensure that create a super class of it? Therefore, the "Link configuration" concept; therefore, we can call other constructors (ie super) from the current constructor. This call is required Java FIRST row of sub-class constructor in order to maintain the hierarchy and to ensure the hierarchy. They assume that if you do not explicitly create parent FIRST (the same as if you forget it), they will provide you implicitly create it.

This check is done during compilation. But I'm not sure what happens at run time, we will get the kind of run-time error, specifically when we try to perform basic functions from the basic structure of the constructor inside the subclass constructor, if not thrown Java compilation error body, rather than from the first line ...


#6th floor

My guess is, this is done in order to make writing Java code processing tools and personnel staff read Java code easier to some extent.

If you allow super()or this()call mobile, there are more changes need to check. For example, if you super()or this()call the move to condition if()it may have to be smart enough to implicitly super()inserted else. If you have two calls super()or together super()and this(), you may need to know how to report errors. You may need to be prohibited method call on the receiver, until the call super()or this()far, and figure out when to become complicated.

Let everyone do this extra work seems to be greater than the price paid.


#7th floor

Therefore, this will not prevent you from performing logic before calling super. It just prevents you from performing logic can not be included in a single expression.

In fact, you can use several methods to perform logic, simply code is packaged in a static function, then you can call it in the super statement.

Using your example:

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}

Building # 8

Tldr:

Other answers to the "cause" of the problem. I will provide some restrictions for this technique :

The basic idea is to use your embedded statement hijacking super statement. This can be a statement by the disguise expression to complete.

Tsdr:

We should consider the call super()prior to Statement9()execution Statement1() super():

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        super(_1, _2, _3); // compiler rejects because this is not the first line
    }
}

Of course, the compiler will reject our code. Therefore, we can do this:

// This compiles fine:

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        super(F(_1), _2, _3);
    }

    public static T1 F(T1 _1) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        return _1;
    }
}

The only restriction is that the parent must have a constructor parameter includes at least one, so that we can dive into the expression as an expression.

This is a more detailed example:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        i = i * 10 - 123;
        if (s.length() > i) {
            s = "This is substr s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        Object obj = Static_Class.A_Static_Method(i, s, t1);
        super(obj, i, "some argument", s, t1, t2); // compiler rejects because this is not the first line
    }
}

Heavy exercise:

// This compiles fine:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        super(Arg1(i, s, t1), Arg2(i), "some argument", Arg4(i, s), t1, Arg6(i, t1));
    }

    private static Object Arg1(int i, String s, T1 t1) {
        i = Arg2(i);
        s = Arg4(s);
        return Static_Class.A_Static_Method(i, s, t1);
    }

    private static int Arg2(int i) {
        i = i * 10 - 123;
        return i;
    }

    private static String Arg4(int i, String s) {
        i = Arg2(i);
        if (s.length() > i) {
            s = "This is sub s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        return s;
    }

    private static T2 Arg6(int i, T1 t1) {
        i = Arg2(i);
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        return t2;
    }
}

In fact, the compiler can automate this process for us. They just chose not to do so.


House # 9

I found a solution.

This does not compile:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
        doSomething(c);
        doSomething2(a);
        doSomething3(b);
    }
}

This effectively:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        this(a + b);
        doSomething2(a);
        doSomething3(b);
    }

    private MySubClass(int c) {
        super(c);
        doSomething(c);
    }
}

#10th floor

I know I'm a bit late to the party, but I have used this technique several times (and I know this is a bit unusual):

I use a method to create a common interface InfoRunnable<T>:

public T run(Object... args);

If you need to do something before it is passed to the constructor, do the following:

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));

House # 11

Before constructor object, you must first create the parent object. As you know, when you write such a class:

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

Go to the next (super extended and hidden):

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

First, we create a Objectthen extended to the object MyClass. We can not Objectcreate before MyClass. A simple rule is that you must call the parent constructor before the sub-constructor. But we know that class can have more than one constructor. Java allows us to choose to be called a constructor (it will be super()or super(yourArgs...)). So, when you write super(yourArgs...)your will redefine the constructor, the constructor will be called to create the parent object. You can not super()perform before other methods, because the object does not exist yet (but super()will create an object, you can do anything you want).

So why not be performed after any method this()it? As you know, this()it is the constructor of the current class. Similarly, our class can have a different number of constructors, and like this()or this(yourArgs...)call them that. As I said, each constructor has a hidden way super(). When we write custom of super(yourArgs...)the time, we super(yourArgs...)removed super() super(yourArgs...). Similarly, when we define this()or this(yourArgs...)we will delete the current constructor us super(), because if super()and this()used together in the same method, it will create more than one parent. That is why this()the reason the same rules applied method. It just creates a parent object passed to another sub-constructor, the constructor calls the super()constructor to create a parent. Therefore, the code will actually look like this:

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 have said, you can execute the following code:

this(a+b);

You can also execute the following code:

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

But you can not perform such code, because your method does not exist:

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;
    }
}

In addition, you must this()have a method chain super()constructor. You can not create an object like this:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}

House # 12

class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

See the example, if we're calling the constructor C(int x)of the z value depends on y, if we are not called in the first row C(), then z will be a problem. z The correct value can not be obtained.


House # 13

The constructor completed its execution was derived by meaningful order. Because the superclass do not know any sub-categories, so any subclass and initialize any initialization it needs to perform the execution are separate, and may be prerequisite. Therefore, it must first complete its execution.

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 of the program is:

Inside A's constructor
Inside B's constructor
Inside C's constructor

House # 14

In fact, super()the first statement of a constructor, because you want to make sure that the superclass has been fully formed before the class constructor. Even if your first statement is not super(), the compiler will add it for you!


House # 15

Could you give a code example, if the compiler does not have this restriction, then bad things 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);
    }
}

Abnormal during construction is almost always said it is constructed object failed to initialize properly, and now is in an error state, can not be used and must be garbage collection. However, the configuration of the subclass negligible abnormality occurs in one of the object and return parent class initialization section. In the example above, if supplied to new Bad()parameter new Bad()0 or greater than 100, essential1and essential2will not initialize properly.

You might say ignore the exception is always a bad idea. Well, this is another example:

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

Funny, is not it? In this example, we want to create as many objects? One? two? Maybe nothing ...

Allowed to invoke the constructor in the middle super()or this()the open configuration function objectionable Pandora box.


On the other hand, I know that calling super()or this()before often need to include some static parts. This may not rely on any thisreference code (in fact, thisa reference already exists in the constructor of the beginning, but at super()or this()can not use ordered before returning) and the need for such calls. In addition, as in any method, it is possible to call super()or this()need to create some local variables before.

In this case, you have the opportunity to:

  1. Use this answer mode is displayed, you can avoid this restriction.
  2. Java allows the team to wait for pre- super()and pre- this()code. This may be of super()or this()to complete a limitation position may occur in the constructor. In fact, even today, a compiler, and can safely be allowed to the extent sufficient to add static code in the constructor begin to distinguish between good and bad (or potentially bad) situation. Indeed, it is assumed super(), and this()returns thisa reference, then, the constructor

return this;

at the end. And reject code compiler

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;
}

Error "may not have been initialized variable x", it can be thisvariable to perform this, just like any other local variables to be checked. The only difference is thiscan not be specified in any other way than by the super()or this()(as usual, if there is no such call in a constructor is called super()implicitly by the compiler inserted at the beginning) and may not be assigned twice. If you have any questions (for example, in the first get(), in fact, xalways been allocated), the compiler may return error. This in any ratio super()or this()in addition to the constructor comment other than simply returns an error better than before.


House # 16

That's because your constructors depend on other constructors. To make your constructor to work, it depends on the normal operation of other other constructors is essential. This is why you must first examine the reasons depend on the constructor, the constructor is called by this () or super () in the constructor. If other constructor calls through this () or super () have a problem, then what else statement is executed, because if the constructor call fails, all statements will fail.


House # 17

I fully agree that restriction is too strong. The method of using an auxiliary static (e.g., Tom Hawtin- line recommended viscosity) all or a single expression "pre-super () calculates" push parameter is not always possible, for example:

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

As Carson Myers suggested, use the "object has not been built" exception will be helpful, but check this exception will slow down the speed of execution each time the object is constructed. I hope that the Java compiler can better distinguish (rather than banned the if statement, but allow use in parameters? Operator), even though this will complicate the language specification.


House # 18

You ask why, the other answers imo does not really say why you can call the constructor of your super, but only if it is the first line. Because you do not really call the constructor. In C ++, the syntax is equivalent

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

When you see such an initialization clause, before the braces, you'll know it's special. It runs before the rest of the constructor run, in fact, run before any member variable initialization. For Java is no different. There is a way to make some code (other constructors) before the constructor real beginning, any member of the subclass before initialization run. That case is to "call" (for example super) on the first line. (In a sense, superor thisa little before the first open parenthesis, even if you typed in the back, as it will execute until you complete a full build everything.) (For example int c = a + b;) the compiler to say, "Oh, good no other constructors, then we can initialize all content. "So it starts running, and what you initialize superclass members and whatnot, and then start executing code after the braces.

If after a few lines, it encountered some code that say, "Oh, yes, when you construct this object, it is my hope that you parameters passed to the constructor of the base class", it was too late, and there is no any reason. Therefore, you will receive a compiler error.


House # 19

By linking constructors and static methods, I found a solution to this problem. I want to do looks like this:

public class Foo extends Baz {
  private final Bar myBar;

  public Foo(String arg1, String arg2) {
    // ...
    // ... Some other stuff needed to construct a 'Bar'...
    // ...
    final Bar b = new Bar(arg1, arg2);
    super(b.baz()):
    myBar = b;
  }
}

Thus, basically based on the configuration parameters of the constructor of an object, the object is stored in a member, and the delivery method on the results of that object to the constructor of super. Enable members to become the final member is also important because of the nature of the class is unchanged. Note that happened by chance, in fact, need to construct Bar several intermediate objects, so in my actual use case, it can not be reduced to a single form.

I finally make it work like this:

public class Foo extends Baz {
  private final Bar myBar;

  private static Bar makeBar(String arg1,  String arg2) {
    // My more complicated setup routine to actually make 'Bar' goes here...
    return new Bar(arg1, arg2);
  }

  public Foo(String arg1, String arg2) {
    this(makeBar(arg1, arg2));
  }

  private Foo(Bar bar) {
    super(bar.baz());
    myBar = bar;
  }
}

Legal code, which completed the task for executing multiple statements before calling the super constructor.

Original articles published 0 · won praise 0 · Views 2243

Guess you like

Origin blog.csdn.net/p15097962069/article/details/103876924