为了账号安全,请及时绑定邮箱和手机立即绑定

为什么这个()和超级()必须是构造函数中的第一个语句?

为什么这个()和超级()必须是构造函数中的第一个语句?

狐的传说 2019-06-12 16:03:06
为什么这个()和超级()必须是构造函数中的第一个语句?Java要求,如果在构造函数中调用了这个()或超级(),那么它必须是第一个语句。为什么?例如: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编译器说:“调用超级必须是构造函数中的第一条语句”。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     }}所以,它是不阻止您执行逻辑在打电话给超级英雄之前。它只是阻止您执行逻辑,您不能适应一个单一的表达式。对于调用,也有类似的规则。this()..编译器说“调用这必须是构造函数中的第一个语句”。为什么编译器有这些限制?您能给出一个代码示例,如果编译器没有这个限制,那么会发生一些不好的事情吗?
查看完整描述

3 回答

?
RISEBY

TA贡献1856条经验 获得超5个赞

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

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

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


查看完整回答
反对 回复 2019-06-12
?
慕村225694

TA贡献1880条经验 获得超4个赞

因为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. ;(

顺便说一句,自Java1.4以来,包含外部的合成字段this在调用内部类超级构造函数之前分配。这引起了NullPointerException编译为针对早期版本的代码中的事件。

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

2018年3月编辑:在讯息中记录:建造和验证Oracle建议取消这一限制(但与C#不同的是,this将是绝对没有分配(Du)构造函数链接之前)。

在历史上,这个()或超级()必须是构造函数中的第一个。这种限制从来不受欢迎,被认为是武断的。有一些微妙的原因,包括特别是对请求的核查,促成了这一限制。多年来,我们已经在VM级别解决了这些问题,以至于考虑取消这个限制变得非常实际,不仅对记录,而且对所有构造函数都是如此。


查看完整回答
反对 回复 2019-06-12
  • 3 回答
  • 0 关注
  • 495 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号