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必须是构造函数中的第一条语句。

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


当前回答

构造函数按照的顺序完成执行是有意义的 推导。因为父类不知道任何子类,任何 它需要执行的初始化与可能的初始化是分开的 子类执行任何初始化的先决条件。 因此,它必须首先完成它的执行。

一个简单的演示:

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

这个程序的输出是:

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

其他回答

我相当肯定(那些熟悉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
    }        
}

构造函数按照的顺序完成执行是有意义的 推导。因为父类不知道任何子类,任何 它需要执行的初始化与可能的初始化是分开的 子类执行任何初始化的先决条件。 因此,它必须首先完成它的执行。

一个简单的演示:

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

这个程序的输出是:

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

因此,它不会阻止您在调用之前执行逻辑 超级。它只是阻止你执行你无法适应的逻辑 变成一个表达式。

实际上,你可以用几个表达式执行逻辑,你只需要把你的代码包装在一个静态函数中,然后在超级语句中调用它。

举个例子:

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

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

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

public T run(Object... args);

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

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

实际上,super()是构造函数的第一个语句,因为要确保在构造子类之前父类已经完全形成。即使在第一个语句中没有super(),编译器也会为你添加它!