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

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


当前回答

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

其他回答

我已经通过链接构造函数和静态方法找到了解决这个问题的方法。我想做的是这样的:

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

基本上是根据构造函数的形参构造一个对象,将对象存储在成员中,并将该对象的方法的结果传递到super的构造函数中。使成员为final也是相当重要的,因为类的性质是不可变的。注意,构造Bar实际上需要一些中间对象,因此在我的实际用例中,它不能简化为一行程序。

我最终做出了这样的工作:

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

合法的代码,它完成了在调用超级构造函数之前执行多条语句的任务。

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

请看例子,如果我们调用构造函数C(int x),那么z的值取决于y,如果我们不在第一行调用C(),那么这将是z的问题,z将无法得到正确的值。

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

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

一个简单的演示:

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

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

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

public T run(Object... args);

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

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