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

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


当前回答

我找到了一个变通办法。

这将不会编译:

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

这是可行的:

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

其他回答

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

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

举个例子:

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

我猜他们这样做是为了让那些编写处理Java代码的工具的人更轻松,在某种程度上也让阅读Java代码的人更轻松。

如果允许super()或this()调用移动,则需要检查更多的变化。例如,如果您将super()或this()调用移动到条件if()中,它可能必须足够聪明,将隐式super()插入到else中。如果调用super()两次,或者同时使用super()和this(),它可能需要知道如何报告错误。它可能需要禁止在接收器上调用方法,直到super()或this()被调用,并计算出何时会变得复杂。

让每个人都做额外的工作似乎是弊大于利。

你问为什么,其他的答案,在我看来,并没有真正说为什么它是可以调用你的超级构造函数,但只有当它是第一行。原因是您并没有真正调用构造函数。在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. 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. (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." So it runs off and initializes your super class and your members and whatnot and then starts executing the code after the open brace.

如果几行之后,它遇到一些代码说“哦,是的,当你构造这个对象时,这是我想让你传递给基类构造函数的参数”,那就太晚了,这没有任何意义。所以你会得到一个编译器错误。

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

这是因为你的构造函数依赖于其他构造函数。要使你的构造函数正常工作,其他构造函数正常工作是必要的,这是依赖的。这就是为什么有必要首先检查由this()或super()在构造函数中调用的依赖构造函数。如果由this()或super()调用的其他构造函数有问题,那么什么点执行其他语句,因为如果被调用的构造函数失败,所有的构造函数都会失败。