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

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


当前回答

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

其他回答

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

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

因为JLS是这么说的。是否可以以兼容的方式更改JLS以允许它?是的。

然而,这会使语言规范变得更加复杂,因为语言规范已经足够复杂了。这不是一件非常有用的事情,并且有一些方法可以绕过它(使用静态方法或lambda表达式this(fn())的结果调用另一个构造函数-该方法在另一个构造函数之前调用,因此也是超级构造函数)。所以改变的功率重量比是不利的。

请注意,该规则本身并不阻止在父类完成构造之前使用字段。

想想这些非法的例子。

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

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

这个例子是合法的,但却是“错误的”。

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

在上面的例子中,如果MyDerived。fn需要MyDerived构造函数的参数,它们需要通过ThreadLocal进行处理。;(

顺便提一下,自Java 1.4以来,包含外部this的合成字段在调用内部类的超构造函数之前被赋值。这在针对早期版本编译的代码中导致了特殊的NullPointerException事件。

还要注意,在存在不安全发布的情况下,构造可以由其他线程重新排序查看,除非采取了预防措施。

编辑2018年3月:在消息记录:构造和验证中,Oracle建议删除此限制(但与c#不同的是,在构造函数链接之前,这将肯定是未分配的(DU))。

从历史上看,this()或super()在构造函数中必须位于第一个。这 限制从来不受欢迎,被认为是武断的。有 一些微妙的原因,包括验证 invokspecial,导致了这种限制。多年来, 我们已经在虚拟机级别解决了这些问题,直到它变成 考虑取消这一限制是可行的,不仅仅是为了记录, 但对于所有的构造函数。

父类的构造函数需要在子类的构造函数之前调用。这将确保如果在构造函数中调用父类上的任何方法,父类已经正确设置。

你要做的是,将参数传递给超级构造函数是完全合法的,你只需要像你所做的那样内联构造这些参数,或者将它们传递给你的构造函数,然后将它们传递给super:

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

如果编译器没有强制执行,你可以这样做:

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

在父类有默认构造函数的情况下,编译器会自动插入对super的调用。由于Java中的每个类都继承自Object,所以必须以某种方式调用Object的构造函数,并且必须首先执行它。编译器可以自动插入super()。强制super首先出现,强制构造函数主体以正确的顺序执行,即:Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

我找到了一个变通办法。

这将不会编译:

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

这是官方回放: 从历史上看,this()或super()在构造函数中必须位于第一个。这 限制从来不受欢迎,被认为是武断的。有一个 一些微妙的原因,包括验证调用特殊, 这导致了这种限制。这些年来,我们已经解决了 这些都是虚拟机级别的,直到它变得实用 考虑取消这一限制,不只是对记录,而是对所有人 构造函数。