Java 为什么this()和super()必须是构造函数中的第一条语句?
Java要求,如果在构造函数中调用this()或super(),它必须是第一条语句。为什么? 例如:Java 为什么this()和super()必须是构造函数中的第一条语句?,java,constructor,Java,Constructor,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 }
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
}
}
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
}
}
public class MySubClassC extends MyClass {
public MySubClassC(Object item) {
// Create a list that contains the item, and pass the list to super
super(createList(item)); // OK
}
private static List createList(item) {
List list = new ArrayList();
list.add(item);
return list;
}
}
class Bad extends Good {
Bad(int n) {
for (int i = 0; i < n; i++)
super(i);
}
}
Sun编译器说“对super的调用必须是构造函数中的第一个语句”。Eclipse编译器说“构造函数调用必须是构造函数中的第一条语句”
但是,您可以通过稍微重新安排代码来解决此问题:
public class MySubClass extends MyClass {
public MySubClass(int a, int b) {
super(a + b); // OK
}
}
下面是另一个例子:
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
}
}
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
}
}
public class MySubClassC extends MyClass {
public MySubClassC(Object item) {
// Create a list that contains the item, and pass the list to super
super(createList(item)); // OK
}
private static List createList(item) {
List list = new ArrayList();
list.add(item);
return list;
}
}
class Bad extends Good {
Bad(int n) {
for (int i = 0; i < n; i++)
super(i);
}
}
因此,它不会阻止您在调用super之前执行逻辑。它只是阻止您执行无法放入单个表达式的逻辑
调用this()
也有类似的规则。编译器说“对它的调用必须是构造函数中的第一条语句”
为什么编译器有这些限制?您能否给出一个代码示例,如果编译器没有此限制,则可能会发生不好的情况?需要在子类“
构造函数”
之前调用父类“构造函数”
。这将确保如果在构造函数中调用父类上的任何方法,则父类已正确设置
您要做的是,将参数传递给超级构造函数是完全合法的,您只需要在执行操作时内联构造这些参数,或者将它们传递给构造函数,然后将它们传递给super
:
public MySubClassB extends MyClass {
public MySubClassB(Object[] myArray) {
super(myArray);
}
}
public class Child extends Parent {
public Child(T1 _1, T2 _2, T3 _3) {
Statement_1();
Statement_2();
Statement_3(); // and etc...
Statement_9();
super(_1, _2, _3); // compiler rejects because this is not the first line
}
}
如果编译器未强制执行此操作,则可以执行以下操作:
public MySubClassB extends MyClass {
public MySubClassB(Object[] myArray) {
someMethodOnSuper(); //ERROR super not yet constructed
super(myArray);
}
}
在父类
具有默认构造函数的情况下,编译器会自动插入对super的调用。由于Java中的每个类都继承自对象
,因此必须以某种方式调用对象构造函数,并且必须首先执行它。编译器自动插入super()允许这样做。强制super首先出现,强制构造函数主体按照正确的顺序执行,即:Object->Parent->Child->ChildOfChild->SoOnSoForth我相当确信(熟悉Java规范的人插话)这是为了防止您(a)被允许使用部分构造的对象,以及(b),强制父类的构造函数在“新鲜”对象上构造
“坏”事情的一些例子是:
class Thing
{
final int x;
Thing(int x) { this.x = x; }
}
class Bad1 extends Thing
{
final int z;
Bad1(int x, int y)
{
this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
super(x);
}
}
class Bad2 extends Thing
{
final int y;
Bad2(int x, int y)
{
this.x = 33;
this.y = y;
super(x); // WHOOPS! x is supposed to be final
}
}
在调用子项的构造函数之前,可以使用匿名初始值设定项块初始化子项中的字段。此示例将演示:
public class Test {
public static void main(String[] args) {
new Child();
}
}
class Parent {
public Parent() {
System.out.println("In parent");
}
}
class Child extends Parent {
{
System.out.println("In initializer");
}
public Child() {
super();
System.out.println("In child");
}
}
这将输出:
在父级中
在初始值设定项中
在儿童中
因为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.
这个例子是合法的,但“错误的”
在上面的示例中,如果MyDerived.fn
需要来自MyDerived
构造函数的参数,则需要使用ThreadLocal
对这些参数进行筛选;(
顺便说一句,自Java 1.4以来,包含外部this
的合成字段在调用内部类超级构造函数之前被赋值。这导致编译为针对早期版本的代码中出现特殊的NullPointerException
事件
另外请注意,如果存在不安全的出版物,除非采取预防措施,否则可以通过其他线程重新排序来查看构造
编辑2018年3月:Oracle在消息中建议删除此限制(但与C#不同,此
在构造函数链接之前肯定是未分配的(DU))
历史上,this()或super()必须是构造函数中的第一个
限制从来都不受欢迎,被认为是武断的
一些微妙的原因,包括
特别是,这导致了这种限制。多年来,
我们已经在虚拟机级别解决了这些问题,直到它成为
切实考虑取消这种限制,不仅仅是为了记录,
但是对于所有的构造函数
我完全同意,限制太强。使用静态辅助方法(如Tom Hawtin-tackline所建议的)或将所有“pre-super()计算”推到参数中的单个表达式中并不总是可行的,例如:
class Sup {
public Sup(final int x_) {
//cheap constructor
}
public Sup(final Sup sup_) {
//expensive copy constructor
}
}
class Sub extends Sup {
private int x;
public Sub(final Sub aSub) {
/* for aSub with aSub.x == 0,
* the expensive copy constructor is unnecessary:
*/
/* if (aSub.x == 0) {
* super(0);
* } else {
* super(aSub);
* }
* above gives error since if-construct before super() is not allowed.
*/
/* super((aSub.x == 0) ? 0 : aSub);
* above gives error since the ?-operator's type is Object
*/
super(aSub); // much slower :(
// further initialization of aSub
}
}
正如Carson Myers所建议的,使用“尚未构造的对象”异常会有所帮助,但在每个对象构造过程中检查该异常会降低执行速度。我倾向于使用能够更好区分的Java编译器(而不是不合理地禁止if语句,但在参数中允许?-运算符)即使这个复杂化了语言规范,你问为什么,而其他答案,IMO,并没有真正地说为什么调用你的超级构造函数是可以的,但是只有当它是第一行的时候。原因是你并没有真正调用构造函数。在C++中,等效语法是
MySubClass: MyClass {
public:
MySubClass(int a, int b): MyClass(a+b)
{
}
};
当您看到initializer子句本身时,在打开大括号之前,您知道它是特殊的。它在任何其他构造函数运行之前运行,事实上在任何成员变量初始化之前运行。对于Java来说没有什么不同。有一种方法可以获得一些代码(其他构造函数)在构造函数真正启动之前,在子类的任何成员初始化之前运行。这种方法是将“调用”(例如super
)放在第一行。(在某种程度上,就是super
public class MySubClass extends MyClass {
public MySubClass(int a, int b) {
this(a + b);
doSomething2(a);
doSomething3(b);
}
private MySubClass(int c) {
super(c);
doSomething(c);
}
}
public T run(Object... args);
super(new InfoRunnable<ThingToPass>() {
public ThingToPass run(Object... args) {
/* do your things here */
}
}.run(/* args here */));
public MyClass {
public MyClass(String someArg) {
System.out.println(someArg);
}
}
public MyClass extends Object{
public MyClass(String someArg) {
super();
System.out.println(someArg);
}
}
public MyClass extends Object{
public MyClass(int a) {
super();
System.out.println(a);
}
public MyClass(int a, int b) {
this(a);
System.out.println(b);
}
}
this(a+b);
public MyClass(int a, SomeObject someObject) {
this(someObject.add(a+5));
}
public MyClass extends Object{
public MyClass(int a) {
}
public MyClass(int a, int b) {
this(add(a, b));
}
public int add(int a, int b){
return a+b;
}
}
public MyClass{
public MyClass(int a) {
this(a, 5);
}
public MyClass(int a, int b) {
this(a);
}
}
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
class Good {
int essential1;
int essential2;
Good(int n) {
if (n > 100)
throw new IllegalArgumentException("n is too large!");
essential1 = 1 / n;
essential2 = n + 2;
}
}
class Bad extends Good {
Bad(int n) {
try {
super(n);
} catch (Exception e) {
// Exception is ignored
}
}
public static void main(String[] args) {
Bad b = new Bad(0);
// b = new Bad(101);
System.out.println(b.essential1 + b.essential2);
}
}
class Bad extends Good {
Bad(int n) {
for (int i = 0; i < n; i++)
super(i);
}
}
return this;
public int get() {
int x;
for (int i = 0; i < 10; i++)
x = i;
return x;
}
public int get(int y) {
int x;
if (y > 0)
x = y;
return x;
}
public int get(boolean b) {
int x;
try {
x = 1;
} catch (Exception e) {
}
return x;
}
public class SomethingComplicated extends SomethingComplicatedParent {
private interface Lambda<T> {
public T run();
}
public SomethingComplicated(Settings settings) {
super(((Lambda<Settings>) () -> {
// My modification code,
settings.setting1 = settings.setting2;
return settings;
}).run());
}
}