为什么this()和super()必须是构造函数中的第一条语句?

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()有类似的规则。 编译器说“调用它必须是构造函数中的第一条语句”。

为什么编译器有这些限制? 你可以给一个代码示例,如果编译器没有这个限制,会发生什么坏事?


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

你所要做的,将参数传递给超级构造函数是完全合法的,你只需要像内联那样构造这些参数,或者将它们传递给构造函数,然后将它们传递给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);
        }
}

parent类具有默认构造函数的情况下, compiler会自动为您插入对super的调用。 由于Java中的每个类都从Object继承,因此必须以某种方式调用对象构造函数,并且必须先执行它。 编译器自动插入super()允许这样做。 强制超级先出现,强制执行构造函数体的顺序是:Object - > Parent - > Child - > ChildOfChild - > SoOnSoForth


我通过链接构造函数和静态方法找到了解决这个问题的方法。 我想做的事情看起来像这样:

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

所以基本上是基于构造函数参数构造一个对象,将该对象存储在成员中,并将该对象的方法结果传递给超类的构造函数。 让成员最终决定也是非常重要的,因为这个类的性质是不变的。 请注意,实际上,构建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;
  }
}

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


因为JLS这样说。 JLS可以以兼容的方式更改以允许它吗? 对。 但是,这会使语言规范复杂化,这已经足够复杂了。 这不是一件非常有用的事情,并且有办法绕过它(用另一个构造函数调用方法this(fn()) - 该方法在另一个构造函数之前调用,因此也是超级构造函数) 。 所以做这个改变的权重比是不利的。

编辑20183月:消息记录:构建和验证Oracle建议删除此限制(但与C#不同,在构造函数链接之前, this将明确未分配(DU))。

历史上,this()或super()必须在构造函数中第一个。 这种限制从来不受欢迎,并且被视为任意。 有许多微妙的原因,包括验证invokespecial,导致了这个限制。 多年来,我们已经在虚拟机级别解决了这些问题,到了考虑解除这一限制变得切实可行的时候,不仅仅是为了记录,而是为了所有构造函数。

链接地址: http://www.djcxy.com/p/59493.html

上一篇: Why do this() and super() have to be the first statement in a constructor?

下一篇: Magento Call custom block inside another custom block