简体   繁体   English

为什么 this() 和 super() 必须是构造函数中的第一条语句?

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

Java requires that if you call this() or super() in a constructor, it must be the first statement. Java 要求如果在构造函数中调用this()super() ,它必须是第一条语句。 Why?为什么?

For example:例如:

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

The Sun compiler says, call to super must be first statement in constructor . Sun 编译器说, call to super must be first statement in constructor The Eclipse compiler says, Constructor call must be the first statement in a constructor . Eclipse 编译器说, Constructor call must be the first statement in a constructor

However, you can get around this by re-arranging the code a little bit:但是,您可以通过稍微重新安排代码来解决此问题:

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

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

So, it is not stopping you from executing logic before the call to super() .因此,它不会阻止您在调用super()之前执行逻辑。 It is just stopping you from executing logic that you can't fit into a single expression.它只是阻止您执行无法放入单个表达式的逻辑。

There are similar rules for calling this() .调用this()也有类似的规则。 The compiler says, call to this must be first statement in constructor .编译器说, call to this must be first statement in constructor

Why does the compiler have these restrictions?为什么编译器有这些限制? Can you give a code example where, if the compiler did not have this restriction, something bad would happen?你能给出一个代码示例,如果编译器没有这个限制,会发生一些不好的事情吗?

The parent class' constructor needs to be called before the subclass' constructor.父类的构造函数需要在子类的构造函数之前调用。 This will ensure that if you call any methods on the parent class in your constructor, the parent class has already been set up correctly.这将确保如果您在构造函数中调用父类的任何方法,则父类已经正确设置。

What you are trying to do, pass args to the super constructor is perfectly legal, you just need to construct those args inline as you are doing, or pass them in to your constructor and then pass them to super :您正在尝试做的事情,将 args 传递给 super 构造函数是完全合法的,您只需要在执行时内联构造这些 args,或者将它们传递给您的构造函数,然后将它们传递给super

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

If the compiler did not enforce this you could do this:如果编译器没有强制执行此操作,您可以这样做:

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

In cases where a parent class has a default constructor the call to super is inserted for you automatically by the compiler.在父类具有默认构造函数的情况下,编译器会自动为您插入对 super 的调用。 Since every class in Java inherits from Object , objects constructor must be called somehow and it must be executed first.由于 Java 中的每个类都继承自Object ,因此必须以某种方式调用对象构造函数,并且必须首先执行它。 The automatic insertion of super() by the compiler allows this.编译器自动插入 super() 允许这样做。 Enforcing super to appear first, enforces that constructor bodies are executed in the correct order which would be: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth强制 super 首先出现,强制构造函数主体以正确的顺序执行,即: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

I've found a way around this by chaining constructors and static methods.我通过链接构造函数和静态方法找到了解决这个问题的方法。 What I wanted to do looked something 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;
  }
}

So basically construct an object based on constructor parameters, store the object in a member, and also pass the result of a method on that object into super's constructor.所以基本上基于构造函数参数构造一个对象,将对象存储在一个成员中,并将对该对象的方法的结果传递给 super 的构造函数。 Making the member final was also reasonably important as the nature of the class is that it's immutable.使成员成为最终成员也相当重要,因为类的性质是它是不可变的。 Note that as it happens, constructing Bar actually takes a few intermediate objects, so it's not reducible to a one-liner in my actual use case.请注意,实际上,构造 Bar 实际上需要一些中间对象,因此在我的实际用例中它不能简化为单行。

I ended up making it work something 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, and it accomplishes the task of executing multiple statements before calling the super constructor.合法的代码,它完成了在调用超级构造函数之前执行多条语句的任务。

Because the JLS says so.因为 JLS 是这么说的。 Could the JLS be changed in a compatible manner to allow it?是否可以以兼容的方式更改 JLS 以允许它? Yup.是的。

However, it would complicate the language spec, which is already more than complicated enough.但是,这会使语言规范复杂化,而这已经足够复杂了。 It wouldn't be a highly useful thing to do and there are ways around it (call another constructor with the result of a static method or lambda expression this(fn()) - the method is called before the other constructor, and hence also the super constructor).这不是一件非常有用的事情,并且有一些方法可以解决它(使用静态方法或 lambda 表达式this(fn())的结果调用另一个构造函数 - 该方法在另一个构造函数之前调用,因此也超级构造函数)。 So the power to weight ratio of doing the change is unfavourable.所以做改变的功率重量比是不利的。

Note that this rule alone does not prevent use of fields before the super class has completed construction.请注意,仅此规则不会阻止在超类完成构造之前使用字段。

Consider these illegal examples.考虑这些非法的例子。

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 "wrong".这个例子是合法的,但是是“错误的”。

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

In the above example, if MyDerived.fn required arguments from the MyDerived constructor they would need to be sleazed through with a ThreadLocal .在上面的示例中,如果MyDerived.fn需要来自MyDerived构造函数的参数,则需要使用ThreadLocal来处理它们。 ;( ;(

Incidentally, since Java 1.4, the synthetic field that contains the outer this is assigned before inner classes super constructor is called.顺便说一下,从 Java 1.4 开始,包含外部this的合成字段是在调用内部类超级构造函数之前分配的。 This caused peculiar NullPointerException events in code compiled to target earlier versions.这会在针对早期版本编译的代码中导致特殊的NullPointerException事件。

Note also, in the presence of unsafe publication, construction can be viewed reordered by other threads, unless precautions are made.另请注意,在存在不安全发布的情况下,除非采取预防措施,否则可以通过其他线程重新排序查看构造。

Edit March 2018: In message Records: construction and validation Oracle is suggesting this restriction be removed (but unlike C#, this will be definitely unassigned (DU) before constructor chaining). 2018 年 3 月编辑:在消息记录中:构造和验证Oracle 建议删除此限制(但与 C# 不同,在构造函数链接之前, this肯定是未分配的(DU))。

Historically, this() or super() must be first in a constructor.从历史上看,this() 或 super() 必须在构造函数中排在第一位。 This restriction was never popular, and perceived as arbitrary.这种限制从未流行过,并且被认为是任意的。 There were a number of subtle reasons, including the verification of invokespecial, that contributed to this restriction.有许多微妙的原因,包括对 invokespecial 的验证,导致了这种限制。 Over the years, we've addressed these at the VM level, to the point where it becomes practical to consider lifting this restriction, not just for records, but for all constructors.多年来,我们已经在 VM 级别解决了这些问题,以至于考虑解除这个限制变得切实可行,不仅是为了记录,而是为了所有构造函数。

Simply because this is the inheritance philosophy.仅仅因为这是继承哲学。 And according to the Java language specification, this is how the constructor's body is defined:根据 Java 语言规范,构造函数的主体是这样定义的:

ConstructorBody: { ExplicitConstructorInvocation opt BlockStatements opt } ConstructorBody: { ExplicitConstructorInvocation opt BlockStatements opt }

The first statement of a constructor body may be either构造函数主体的第一条语句可以是

  • an explicit invocation of another constructor of the same class (by using the keyword "this");显式调用同一类的另一个构造函数(使用关键字“this”); or或者
  • an explicit invocation of the direct superclass (by using the keyword "super")直接超类的显式调用(通过使用关键字“super”)

If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.如果构造函数体不是以显式构造函数调用开始,并且被声明的构造函数不是原始类 Object 的一部分,则构造函数体隐式以超类构造函数调用“super();”开始,调用它的直接超类不带参数。 And so on.. there will be a whole chain of constructors called all the way back to the constructor of Object;依此类推.. 将有一个完整的构造函数链一直调用到 Object 的构造函数; "All Classes in the Java platform are Descendants of Object". “Java 平台中的所有类都是对象的后代”。 This thing is called " Constructor Chaining ".这个东西被称为“构造函数链接”。

Now why is this?为什么会这样?
And the reason why Java defined the ConstructorBody in this way, is that they needed to maintain the hierarchy of the object.而Java之所以以这种方式定义ConstructorBody,是因为它们需要维护对象的层次结构 Remember the definition of the inheritance;记住继承的定义; It's extending a class.它正在扩展一个类。 With that being said, you cannot extend something that doesn't exist.话虽如此,你不能扩展不存在的东西。 The base (the superclass) needs to be created first, then you can derive it (the subclass).需要先创建基类(超类),然后才能派生它(子类)。 That's why they called them Parent and Child classes;这就是为什么他们称它们为 Parent 和 Child 类; you can't have a child without a parent.没有父母就不能生孩子。

On a technical level, a subclass inherits all the members (fields, methods, nested classes) from its parent.在技​​术层面上,子类从其父类继承所有成员(字段、方法、嵌套类)。 And since Constructors are NOT members (They don't belong to objects. They are responsible of creating objects) so they are NOT inherited by subclasses, but they can be invoked.而且由于构造函数不是成员(它们不属于对象。它们负责创建对象),因此它们不会被子类继承,但可以调用它们。 And since at the time of object creation only ONE constructor is executed .而且由于在创建对象时只执行了一个构造函数 So how do we guarantee the creation of the superclass when you create the subclass object?那么我们如何在创建子类对象的时候保证超类的创建呢? Thus the concept of "constructor chaining";因此,“构造函数链接”的概念; so we have the ability to invoke other constructors (ie super) from within the current constructor.所以我们有能力从当前构造函数中调用其他构造函数(即super)。 And Java required this invocation to be the FIRST line in the subclass constructor to maintain the hierarchy and guarantee it. Java 要求此调用是子类构造函数中的第一行,以维护和保证层次结构。 They assume that if you don't explicitly create the parent object FIRST (like if you forgot about it), they will do it implicitly for you.他们假设如果您不首先显式创建父对象(就像您忘记了它一样),他们会为您隐式创建。

This check is done during compilation.此检查在编译期间完成。 But I'm not sure what would happen on runtime, what kind of runtime error we would get, IF Java doesn't throw a compile-error when we explicitly try to execute a base constructor from within a subclass's constructor in the middle of its body and not from the very first line ...但是我不确定运行时会发生什么,我们会得到什么样的运行时错误,如果 Java 在我们显式尝试从其中间的子类的构造函数中执行基本构造函数时没有抛出编译错误身体而不是从第一行开始......

I am fairly sure (those familiar with the Java Specification chime in) that it is to prevent you from (a) being allowed to use a partially-constructed object, and (b), forcing the parent class's constructor to construct on a "fresh" object.我相当确定(那些熟悉 Java 规范的人)这是为了防止您(a)被允许使用部分构造的对象,以及(b)强制父类的构造函数在“新鲜“ 目的。

Some examples of a "bad" thing would be: “坏”事情的一些例子是:

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

You asked why, and the other answers, imo, don't really say why it's ok to call your super's constructor, but only if it's the very first line.您问为什么,而其他答案,imo,并没有真正说明为什么可以调用您的超级构造函数,但前提是它是第一行。 The reason is that you're not really calling the constructor.原因是您并没有真正调用构造函数。 In C++, the equivalent syntax is在 C++ 中,等价的语法是

MySubClass: MyClass {

public:

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

};

When you see the initializer clause on its own like that, before the open brace, you know it's special.当您像这样在左大括号之前看到初始化器子句时,您就知道它很特别。 It runs before any of the rest of the constructor runs and in fact before any of the member variables are initialized.它在任何其他构造函数运行之前运行,实际上在任何成员变量被初始化之前运行。 It's not that different for Java. Java 并没有什么不同。 There's a way to get some code (other constructors) to run before the constructor really starts, before any members of the subclass are initialized.有一种方法可以让一些代码(其他构造函数)在构造函数真正开始之前运行,在子类的任何成员被初始化之前。 And that way is to put the "call" (eg super ) on the very first line.这种方式就是将“呼叫”(例如super )放在第一行。 (In a way, that super or this is kind of before the first open brace, even though you type it after, because it will be executed before you get to the point that everything is fully constructed.) Any other code after the open brace (like int c = a + b; ) makes the compiler say "oh, ok, no other constructors, we can initialize everything then." (在某种程度上, superthis有点在第一个大括号之前,即使您在之后键入它,因为它将在您到达所有内容完全构造之前执行。)大括号之后的任何其他代码(如int c = a + b; )使编译器说“哦,好的,没有其他构造函数,我们可以初始化所有内容。” So it runs off and initializes your super class and your members and whatnot and then starts executing the code after the open brace.所以它运行并初始化你的超类和你的成员等等,然后在左大括号之后开始执行代码。

If, a few lines later, it meets some code saying "oh yeah when you're constructing this object, here are the parameters I want you to pass along to the constructor for the base class", it's too late and it doesn't make any sense.如果在几行之后,它遇到一些代码说“哦,是的,当你构造这个对象时,这是我希望你传递给基类的构造函数的参数”,那就太晚了,它不会有任何意义。 So you get a compiler error.所以你得到一个编译器错误。

So, it is not stopping you from executing logic before the call to super.因此,它不会阻止您在调用 super 之前执行逻辑。 It is just stopping you from executing logic that you can't fit into a single expression.它只是阻止您执行无法放入单个表达式的逻辑。

Actually you can execute logic with several expessions, you just have to wrap your code in a static function and call it in the super statement.实际上,您可以使用多个表达式执行逻辑,您只需将代码包装在静态函数中并在 super 语句中调用它。

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

I totally agree, the restrictions are too strong.我完全同意,限制太强了。 Using a static helper method (as Tom Hawtin - tackline suggested) or shoving all "pre-super() computations" into a single expression in the parameter is not always possible, eg:使用静态辅助方法(如 Tom Hawtin - tackline 建议的那样)或将所有“pre-super() 计算”推入参数中的单个表达式并不总是可行的,例如:

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

Using an "object not yet constructed" exception, as Carson Myers suggested, would help, but checking this during each object construction would slow down execution.正如 Carson Myers 所建议的,使用“对象尚未构造”异常会有所帮助,但在每个对象构造期间检查此异常会减慢执行速度。 I would favor a Java compiler that makes a better differentiation (instead of inconsequently forbidding an if-statement but allowing the ?-operator within the parameter), even if this complicates the language spec.我更喜欢 Java 编译器,它可以做出更好的区分(而不是间接禁止 if 语句但允许参数中的 ? 运算符),即使这会使语言规范复杂化。

My guess is they did this to make life easier for people writing tools that process Java code, and to some lesser degree also people who are reading Java code.我的猜测是,他们这样做是为了让编写处理 Java 代码的工具的人的生活更轻松,在某种程度上也让阅读 Java 代码的人更轻松。

If you allow the super() or this() call to move around, there are more variations to check for.如果您允许super()this()调用移动,则需要检查更多变体。 For example if you move the super() or this() call into a conditional if() it might have to be smart enough to insert an implicit super() into the else .例如,如果您将super()this()调用移动到条件if()中,它可能必须足够聪明才能将隐式super()插入到else中。 It might need to know how to report an error if you call super() twice, or use super() and this() together.如果你调用super()两次,或者同时使用super()this() ,它可能需要知道如何报告错误。 It might need to disallow method calls on the receiver until super() or this() is called and figuring out when that is becomes complicated.它可能需要在调用super()this()之前禁止接收器上的方法调用,并确定何时变得复杂。

Making everyone do this extra work probably seemed like a greater cost than benefit.让每个人都做这些额外的工作似乎是成本大于收益。

I found a woraround.我找到了一个解决方法。

This won't 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 works :这有效:

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

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.在上面的例子中,如果给new Bad()的参数是 0 或大于 100,那么essential1essential2都没有被正确初始化。

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.允许在构造函数的中间调用super()this()将打开一个由令人发指的构造函数组成的潘多拉魔盒。


On the other hand, I understand a frequent need to include some static part before a call to super() or this() .另一方面,我理解在调用super()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.这可能是任何不依赖this引用的代码(实际上,它已经存在于构造函数的开头,但在super()this()返回之前不能有序使用)并且需要进行此类调用。 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.此外,就像在任何方法中一样,在调用super()this()之前创建的一些局部变量可能会在它之后需要。

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.等待 Java 团队允许 pre- super()和 pre this()代码。 It may be done by imposing a restriction on where super() or this() may occur in a constructor.这可以通过对super()this()可能出现在构造函数中的位置施加限制来完成。 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事实上,假设super()this()返回this引用,反过来,你的构造函数有

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.如果出现错误“变量 x 可能尚未初始化”,它可以对this变量执行此操作,就像对任何其他局部变量一样对其进行检查。 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.唯一的区别是this不能通过super()this()调用以外的任何方式分配(并且,像往常一样,如果在构造函数中没有这样的调用,编译器会在开头隐式插入super() )和可能不会被分配两次。 In case of any doubt (like in the first get() , where x is actually always assigned), the compiler could return an error.如果有任何疑问(例如在第一个get()中,实际上总是分配x ),编译器可能会返回错误。 That would be better than simply return error on any constructor where there is something except a comment before super() or this() .这比简单地在除了super()this()之前的注释之外的任何构造函数上返回错误要好。

You can use anonymous initializer blocks to initialize fields in the child before calling it's constructor.您可以在调用其构造函数之前使用匿名初始化程序块来初始化子项中的字段。 This example will demonstrate :这个例子将演示:

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 parent在父母
In initializer在初始化器中
In child在儿童

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

I know I am a little late to the party, but I've used this trick a couple of times (and I know it's a bit unusual):我知道我参加聚会有点晚了,但我已经使用过几次这个技巧(而且我知道这有点不寻常):

I create an generic interface InfoRunnable<T> with one method:我用一种方法创建了一个通用接口InfoRunnable<T>

public T run(Object... args);

And if I need to do something before passing it to the constructor I just do this:如果我需要在将它传递给构造函数之前做一些事情,我就这样做:

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

Actually, super() is the first statement of a constructor because to make sure its superclass is fully-formed before the subclass being constructed.实际上, super()是构造函数的第一条语句,因为要确保在构造子类之前它的超类是完全形成的。 Even if you don't have super() in your first statement, the compiler will add it for you!即使您的第一条语句中没有super() ,编译器也会为您添加它!

That's because your constructor depends on other constructors.那是因为您的构造函数依赖于其他构造函数。 To your constructor work correctly its necessary to other constructor works correctly which is dependent.要使您的构造函数正常工作,其他构造函数必须正常工作,这是依赖的。 That's why its necessary to check dependent constructors first which called by either this() or super() in your constructor.这就是为什么有必要首先检查在构造函数中由 this() 或 super() 调用的依赖构造函数。 If other constructors which called by either this() or super() have a problem so whats point execute other statements because all will fail if called constructor fails.如果 this() 或 super() 调用的其他构造函数有问题,那么点执行其他语句,因为如果调用的构造函数失败,所有语句都会失败。

The question of why Java does this has already been answered, but since I stumbled upon this question hoping to find a better alternative to the one-liner, I'll hereby share my work-around: Java为什么这样做的问题已经得到解答,但是由于我偶然发现了这个问题,希望找到一个更好的替代单线的方法,因此我将分享我的解决方法:

public class SomethingComplicated extends SomethingComplicatedParent {

    private interface Lambda<T> {
        public T run();
    }

    public SomethingComplicated(Settings settings) {
        super(((Lambda<Settings>) () -> {

            // My modification code,
            settings.setting1 = settings.setting2;
            return settings;
        }).run());
    }
}

Calling a static function should perform better, but I would use this if I insist on having the code "inside" the constructor, or if I have to alter multiple parameters and find defining many static methods bad for readability.调用静态函数应该执行得更好,但如果我坚持将代码“放在”构造函数中,或者如果我必须更改多个参数并发现定义许多静态方法不利于可读性,我会使用它。

Tldr:语言:

The other answers have tackled the "why" of the question.其他答案已经解决了问题的“为什么”。 I'll provide a hack around this limitation:我将提供一个解决此限制的技巧

The basic idea is to hijack the super statement with your embedded statements.基本思想是用嵌入的语句劫持super语句。 This can be done by disguising your statements as expressions .这可以通过将您的语句伪装成表达式来完成。

Tsdr:管家:

Consider we want to do Statement1() to Statement9() before we call super() :考虑我们想在调用super() Statement9() ) 执行Statement1() ) :

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

The compiler will of course reject our code.编译器当然会拒绝我们的代码。 So instead, 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 limitation is that the parent class must have a constructor which takes in at least one argument so that we can sneak in our statement as an expression.唯一的限制是父类必须有一个构造函数,它至少接受一个参数,以便我们可以将语句作为表达式潜入。

Here is a more elaborate 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
    }
}

Reworked into:改造成:

// 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, compilers could have automated this process for us.事实上,编译器可以为我们自动化这个过程。 They'd just chosen not to.他们只是选择不这样做。

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 .首先我们创建一个Object ,然后将此对象扩展到MyClass We can not create MyClass before the Object .我们不能在Object之前创建MyClass 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...) ). Java 允许我们选择一个将被调用的构造函数(它将是super()super(yourArgs...) )。 So, when you write super(yourArgs...) you redefine constructor which will be called to create a parent object.因此,当您编写super(yourArgs...)时,您重新定义了构造函数,该构造函数将被调用以创建父对象。 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).您不能在super()之前执行其他方法,因为该对象尚不存在(但在super()之后将创建一个对象,您将能够做任何您想做的事情)。

So why then we cannot execute this() after any method?那么为什么我们不能在任何方法之后执行this()呢? As you know this() is the constructor of the current class.如您所知this()是当前类的构造函数。 Also we can have different number of constructors in our class and call them like this() or this(yourArgs...) .我们也可以在我们的类中有不同数量的构造函数,并像this()this(yourArgs...)一样调用它们。 As I said every constructor has hidden method super() .正如我所说,每个构造函数都有隐藏方法super() When we write our custom super(yourArgs...) we remove super() with super(yourArgs...) .当我们编写自定义super(yourArgs...)时,我们使用super(yourArgs...) yourArgs...) 删除super() )。 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.此外,当我们定义this()this(yourArgs...)时,我们还会在当前构造函数中删除我们的super() ,因为如果super()this()在同一方法中,它将创建多个父对象。 That is why the same rules imposed for this() method.这就是为什么对this()方法施加相同规则的原因。 It just retransmits parent object creation to another child constructor and that constructor calls super() constructor for parent creation.它只是将父对象的创建重新传输到另一个子构造函数,并且该构造函数调用super()构造函数来创建父对象。 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.此外,您必须在this()方法链中拥有super()构造函数。 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);
        }
}
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 are calling the constructor C(int x) then value of z is depend on y if we do not call C() in the first line then it will be the problem for z.看例子如果我们调用构造函数C(int x)那么 z 的值取决于 y 如果我们不在第一行调用C()那么这将是 z 的问题。 z would not be able to get correct value. z 将无法获得正确的值。

The main goal of adding the super() in the sub-class constructors is that the main job of the compiler is to make a direct or indirect connection of all the classes with the Object class that's why the compiler checks if we have provided the super(parameterized) then compiler doesn't take any responsibility.在子类构造函数中添加 super() 的主要目的是编译器的主要工作是将所有类与 Object 类直接或间接连接,这就是编译器检查我们是否提供了 super (参数化)然后编译器不承担任何责任。 so that all the instance member gets initialized from Object to the sub - classes.以便所有实例成员都从 Object 初始化到子类。

This is official replay: Historically, this() or super() must be first in a constructor.这是官方重播:从历史上看,this() 或 super() 必须在构造函数中排在第一位。 This这个
restriction was never popular, and perceived as arbitrary.限制从未流行,并被认为是任意的。 There were a有一个
number of subtle reasons, including the verification of invokespecial,一些微妙的原因,包括invokespecial的验证,
that contributed to this restriction.这促成了这种限制。 Over the years, we've addressed多年来,我们解决了
these at the VM level, to the point where it becomes practical to这些在 VM 级别,到了实际可行的程度
consider lifting this restriction, not just for records, but for all考虑取消这个限制,不仅是为了记录,而是为了所有人
constructors.构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Super认为它不是声明中的第一个构造函数 - Super thinks it is not the first constructor in a statement 构造函数调用必须是 super() 构造函数中的第一条语句 - Constructor call must be the first statement in a constructor in super() 对super()的调用必须是构造函数体中的第一个语句 - call to super() must be the first statement in constructor body 对 super 的调用必须是构造函数中的第一条语句,但它是 - Call to super must be first statement in the constructor, but it is 错误:超级必须是构造函数中的第一条语句 - error: Super must be first statement in constructor 为什么java编译器在第一条语句super()之后复制构造函数中的实例初始化块? - why java compiler copies the instance initializer block in the constructor after the first statement super()? 使用super()时,构造函数调用必须是构造函数中的第一条语句; - Constructor Call must be the first statement in a constructor while using super(); 构造函数调用是否必须是构造函数中的第一条语句? - Does the constructor call have to be the first statement in the constructor? 为什么我们在构造函数中调用super() - Why do we call super() in constructor Zebra.java:3:错误:对super的调用必须是构造函数中的第一条语句 - Zebra.java:3: error: call to super must be first statement in constructor
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM