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必须是构造函数中的第一条语句。
为什么编译器有这些限制?你能给出一个代码例子,如果编译器没有这个限制,就会发生不好的事情吗?
我已经通过链接构造函数和静态方法找到了解决这个问题的方法。我想做的是这样的:
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;
}
}
合法的代码,它完成了在调用超级构造函数之前执行多条语句的任务。
因为这就是传承哲学。根据Java语言规范,构造函数体是这样定义的:
ConstructorBody:
{ExplicitConstructorInvocationopt BlockStatementsopt}
构造函数体的第一个语句可以是任意一个
显式调用同一类的另一个构造函数(通过使用关键字“this”);或
直接超类的显式调用(通过使用关键字"super")
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments. And so on.. there will be a whole chain of constructors called all the way back to the constructor of Object; "All Classes in the Java platform are Descendants of Object". This thing is called "Constructor Chaining".
为什么会这样?
Java以这种方式定义ConstructorBody的原因是,他们需要维护对象的层次结构。记住继承的定义;它扩展了一个类。话虽如此,你不能扩展不存在的东西。首先需要创建基类(超类),然后才能派生它(子类)。这就是为什么他们称它们为父类和子类;你不能没有父母就有孩子。
On a technical level, a subclass inherits all the members (fields, methods, nested classes) from its parent. And since Constructors are NOT members (They don't belong to objects. They are responsible of creating objects) so they are NOT inherited by subclasses, but they can be invoked. And since at the time of object creation only ONE constructor is executed. So how do we guarantee the creation of the superclass when you create the subclass object? Thus the concept of "constructor chaining"; so we have the ability to invoke other constructors (i.e. super) from within the current constructor. And Java required this invocation to be the FIRST line in the subclass constructor to maintain the hierarchy and guarantee it. They assume that if you don't explicitly create the parent object FIRST (like if you forgot about it), they will do it implicitly for you.
该检查在编译期间进行。但是我不确定在运行时会发生什么,我们会得到什么样的运行时错误,如果Java没有抛出一个编译错误,当我们显式地试图从子类的构造函数中执行一个基本构造函数时,在它的主体中间,而不是从第一行开始……