Java要求,如果在构造函数中调用this()或super(),它必须是第一条语句。为什么?

例如:

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编译器说,调用super必须是构造函数中的第一条语句。Eclipse编译器说,构造函数调用必须是构造函数中的第一个语句。

然而,你可以通过稍微重新安排代码来解决这个问题:

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

下面是另一个例子:

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

因此,它不会阻止您在调用super()之前执行逻辑。它只是阻止您执行无法放入单个表达式中的逻辑。

调用this()也有类似的规则。编译器说,调用this必须是构造函数中的第一条语句。

为什么编译器有这些限制?你能给出一个代码例子,如果编译器没有这个限制,就会发生不好的事情吗?


当前回答

我相当肯定(那些熟悉Java规范的人会说),它是为了防止你(a)被允许使用部分构造的对象,(b)强迫父类的构造函数在一个“新”对象上构造。

“坏”事情的一些例子是:

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

其他回答

在构造子对象之前,必须先创建父对象。 如你所知,当你这样写类时:

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

它转向下一个(extend和super只是被隐藏了):

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

正如其他人所说,你可以这样执行代码:

this(a+b);

你也可以像这样执行代码:

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

但是你不能像这样执行代码,因为你的方法还不存在:

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

此外,在this()方法链中必须有super()构造函数。你不能像这样创建一个对象:

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

在调用子对象的构造函数之前,可以使用匿名初始化块初始化子对象中的字段。这个例子将演示:

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

这将输出:

在父 在初始化 在儿童

在子类构造函数中添加super()的主要目标是编译器的主要工作是将所有类与Object类建立直接或间接的连接,这就是为什么编译器检查我们是否提供了super(参数化),然后编译器不承担任何责任。 这样所有的实例成员从Object初始化为子类。

因为这就是传承哲学。根据Java语言规范,构造函数体是这样定义的:

ConstructorBody: {ExplicitConstructorInvocationopt BlockStatementsopt}

构造函数体的第一个语句可以是任意一个

显式调用同一类的另一个构造函数(通过使用关键字“this”);或 直接超类的显式调用(通过使用关键字"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. And so on.. there will be a whole chain of constructors called all the way back to the constructor of Object; "All Classes in the Java platform are Descendants of Object". This thing is called "Constructor Chaining".

为什么会这样? Java以这种方式定义ConstructorBody的原因是,他们需要维护对象的层次结构。记住继承的定义;它扩展了一个类。话虽如此,你不能扩展不存在的东西。首先需要创建基类(超类),然后才能派生它(子类)。这就是为什么他们称它们为父类和子类;你不能没有父母就有孩子。

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 (i.e. super) from within the current constructor. And Java required this invocation to be the FIRST line in the subclass constructor to maintain the hierarchy and guarantee it. 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.

该检查在编译期间进行。但是我不确定在运行时会发生什么,我们会得到什么样的运行时错误,如果Java没有抛出一个编译错误,当我们显式地试图从子类的构造函数中执行一个基本构造函数时,在它的主体中间,而不是从第一行开始……

我知道我有点晚了,但我已经用过几次这个技巧了(我知道这有点不寻常):

我用一个方法创建了一个泛型接口InfoRunnable<T>:

public T run(Object... args);

如果我需要在把它传递给构造函数之前做一些事情,我只需要这样做:

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