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

2022-08-31 04:08:33

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 编译器说, .Eclipse 编译器说, .call to super must be first statement in constructorConstructor call must be the first statement in a constructor

但是,您可以通过稍微重新排列代码来解决此问题:

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()call to this must be first statement in constructor

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


答案 1

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

你试图做的是,将args传递给超级构造函数是完全合法的,你只需要像你一样内联构造这些args,或者将它们传递给你的构造函数,然后将它们传递给: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中的每个类都继承自,所以必须以某种方式调用对象构造函数,并且必须首先执行它。编译器自动插入 super() 允许这样做。强制 super 首先出现,强制构造函数体以正确的顺序执行,该顺序为:对象 -> 父级 ->子项 ->子项 -> SoOnSoForthObject


答案 2

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

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 的构造函数。使成员成为最终成员也相当重要,因为类的本质是它是不可变的。请注意,碰巧的是,构造 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;
  }
}

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